Android: cho phép chụp dọc và ngang cho máy tính bảng, nhưng ép chân dung trên điện thoại?


191

Tôi muốn máy tính bảng có thể hiển thị ở chế độ dọc và ngang (sw600dp trở lên), nhưng điện thoại chỉ bị giới hạn ở chế độ dọc. Tôi không thể tìm thấy bất kỳ cách nào để chọn một cách có điều kiện. Bất kỳ đề xuất?


Một cách sẽ là KHÔNG thiết kế bố cục ngang cho điện thoại, như khi sử dụng thư mục layout-landbên trong res.
Ghost

12
Điều đó sẽ chỉ khiến bố cục chân dung hiển thị trong phong cảnh. Nó sẽ không thực sự ngăn điện thoại quay sang cảnh quan.
radley

Câu trả lời:


446

Đây là một cách tốt để sử dụng tài nguyênvòng loại kích thước .

Đặt tài nguyên bool này trong res / value dưới dạng bools.xml hoặc bất cứ điều gì (tên tệp không quan trọng ở đây):

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <bool name="portrait_only">true</bool>
    </resources>

Đặt cái này trong res / value-sw600dp và res / value-xlarge:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <bool name="portrait_only">false</bool>
    </resources>

Xem câu trả lời bổ sung này để được trợ giúp thêm các thư mục và tệp này trong Android Studio.

Sau đó, trong phương thức onCreate của Hoạt động, bạn có thể thực hiện việc này:

    if(getResources().getBoolean(R.bool.portrait_only)){
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }

Thiết bị hơn 600 dp theo hướng chiều rộng nhỏ nhất, hoặc x-lớn trên các thiết bị pre-Android 3.2 (viên nén, về cơ bản) sẽ cư xử như bình thường, dựa trên cảm biến và xoay người dùng khóa, vv . Mọi thứ khác (điện thoại, khá nhiều) sẽ chỉ là chân dung.


1
Tôi có cần sử dụng xlargequá hay tôi chỉ có thể sử dụng sw600dp? Có lẽ không có máy tính bảng nào chạy <3,2 trong những ngày này.
theblang

7
Chà, điều này có khả năng khởi động lại hoạt động khi bắt đầu trong cảnh quan, xem developer.android.com/reference/android/app/iêu
Bondax

1
@Bondax Nhận xét đó chỉ áp dụng "Nếu hoạt động hiện đang ở phía trước hoặc ảnh hưởng đến hướng màn hình", điều này không thể xảy ra vì chúng tôi đang ở trong OnCreate tại thời điểm này.
Brian Christensen

1
@BrianChristensen Tôi quan sát thấy khởi động lại một hoạt động khi cài đặt hướng được yêu cầu onCreate(). Khi tôi chuyển cuộc gọi đến setRequestedOrientation()một vị trí khác theo thời gian và bối cảnh, việc khởi động lại đã không xảy ra nữa.
Bondax

8
nếu tôi khởi động ứng dụng ở chế độ ngang, nó vẫn hiển thị chế độ ngang trong giây lát.
最 白

29

Trước tiên, bạn có thể thử cách này để có được kích thước màn hình của thiết bị

if ((getResources().getConfiguration().screenLayout &      Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE) {     
    Toast.makeText(this, "Large screen",Toast.LENGTH_LONG).show();

}
else if ((getResources().getConfiguration().screenLayout &      Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_NORMAL) {     
    Toast.makeText(this, "Normal sized screen" , Toast.LENGTH_LONG).show();

} 
else if ((getResources().getConfiguration().screenLayout &      Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_SMALL) {     
    Toast.makeText(this, "Small sized screen" , Toast.LENGTH_LONG).show();
}
else {
    Toast.makeText(this, "Screen size is neither large, normal or small" , Toast.LENGTH_LONG).show();
}

và sau đó thiết lập định hướng theo đó

setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

4
Tôi nên đặt phương thức setRequestedOrientation () nào? Khi onCreate đã bắt đầu, nó đã chọn hướng mà nó muốn.
Kenny Wyland

kenny kiểm tra cái này tôi nghĩ nó có thể giúp bạn stackoverflow.com/questions/2833474/ Kẻ
Avi Kumar Manku

Tôi thấy câu trả lời này đã được nâng cấp, nhưng tôi không chắc nó giải quyết được vấn đề. Configurationcung cấp screenLayoutthông tin - DPI. Trong khi câu hỏi liên quan đến kích thước màn hình thiết bị vật lý - điện thoại máy tính bảng VS. Có nghĩa là, bạn có thể có một Configuration.SCREENLAYOUT_SIZE_NORMALvà nó sẽ là một máy tính bảng MDPI.
đánh dấu vào

tôi phải đối mặt với vấn đề stackoverflow.com/questions/42172864/ từ
Aditya Vyas-Lakhan

10

Bổ sung cho câu trả lời được chấp nhận

Bạn có thể thực hiện các bước sau trong Android Studio để thêm res/values-sw600dpres/values-largethư mục với các bools.xmltệp của họ .

giá trị-sw600dp

Trước hết, từ tab Project, chọn bộ lọc Project (chứ không phải Android) trong bộ điều hướng.

nhập mô tả hình ảnh ở đây

Sau đó nhấp chuột phải vào app/src/main/resthư mục. Chọn Mới > Thư mục tài nguyên Android .

Chọn Chiều rộng màn hình nhỏ nhất , rồi nhấn nút >> .

nhập mô tả hình ảnh ở đây

Nhập vào 600cho chiều rộng màn hình nhỏ nhất. Tên thư mục sẽ được tạo tự động. Nói OK.

nhập mô tả hình ảnh ở đây

Sau đó nhấp chuột phải vào values-sw600dptập tin vừa tạo . Chọn Mới > Giá trị tệp tài nguyên . Nhập boolstên.

giá trị lớn

Việc thêm một values-largethư mục chỉ cần thiết nếu bạn đang hỗ trợ Android 3.2 (API cấp 13). Nếu không, bạn có thể bỏ qua bước này. Thư mục values-largetương ứng với values-sw600dp. ( values-xlargetương ứng với values-sw720dp.)

Để tạo values-largethư mục, hãy làm theo các bước tương tự như trên, nhưng trong trường hợp này, chọn Kích thước thay vì Chiều rộng màn hình nhỏ nhất. Chọn Lớn . Tên thư mục sẽ được tạo tự động.

nhập mô tả hình ảnh ở đây

Nhấp chuột phải vào thư mục như trước để tạo bools.xmltập tin.


2
Hãy cho tôi biết nếu điều này không hoạt động và tôi sẽ sửa nó. Nó làm việc cho tôi. Tôi có một downvote nhưng không có bình luận vì vậy tôi không biết phải sửa gì.
Suragch

9

Đây là cách tôi đã làm (lấy cảm hứng từ http://androidblogger.blogspot.com/2011/08/orientation-for-both-phones-and-tablets.html ):

Trong AndroidManifest.xml, đối với từng hoạt động bạn muốn có thể thay đổi giữa dọc và ngang (đảm bảo bạn thêm screenSize - bạn không cần sử dụng điều này!) Bạn không cần đặt hướng màn hình ở đây. :

android:configChanges="keyboardHidden|orientation|screenSize"

Các phương thức để thêm vào trong mỗi Hoạt động:

public static boolean isXLargeScreen(Context context) {
    return (context.getResources().getConfiguration().screenLayout
    & Configuration.SCREENLAYOUT_SIZE_MASK)
    >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
} 

và: (nếu bạn không ghi đè phương thức này, ứng dụng sẽ gọi onCreate () khi thay đổi hướng)

@Override
public void onConfigurationChanged (Configuration newConfig)
{       
    super.onConfigurationChanged(newConfig);

    if (!isXLargeScreen(getApplicationContext()) ) {            
        return; //keep in portrait mode if a phone      
    }

    //I set background images for landscape and portrait here
}

Trong onCreate () của mỗi Hoạt động:

if (!isXLargeScreen(getApplicationContext())) { //set phones to portrait; 
   setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);            
}
else {
  //I set background images here depending on portrait or landscape orientation 
}

Điều duy nhất tôi dường như không thể tìm ra là làm thế nào để ứng dụng thay đổi các tệp bố cục khi chuyển từ ngang sang dọc hoặc ngược lại. Tôi giả sử câu trả lời đang làm một cái gì đó tương tự như những gì liên kết ở trên làm, nhưng tôi không thể làm cho nó hoạt động với tôi - nó đã xóa tất cả dữ liệu của tôi. Nhưng nếu bạn có một ứng dụng đủ đơn giản mà bạn có cùng tệp bố cục cho dọc và ngang, thì ứng dụng này sẽ hoạt động.


2
đặt bố cục cảnh quan trong thư mục đất bố trí của bạn
radley

Bạn sẽ nhận được một ngoại lệ nếu bạn không gọi super onConfigurationChanged
RominaV

8

Theo câu trả lời của Ginny , tôi nghĩ cách đáng tin cậy nhất để làm điều đó là như sau:

Như được mô tả ở đây , đặt một boolean trong tài nguyên sw600dp. Nó phải có tiền tố sw nếu không nó sẽ không hoạt động đúng:

trong res / value-sw600dp / dimens.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="isTablet">true</bool>
</resources>

trong res / value / dimens.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="isTablet">false</bool>
</resources>

Sau đó thực hiện một phương thức để lấy boolean đó:

public class ViewUtils {
    public static boolean isTablet(Context context){
        return context.getResources().getBoolean(R.bool.isTablet);
    }
}

Và một hoạt động cơ bản để mở rộng từ các hoạt động mà bạn muốn hành vi này:

public abstract class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (!ViewUtils.isTablet(this)) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
    }
}

Vì vậy, mỗi hoạt động sẽ mở rộng BaseActivity:

public class LoginActivity extends BaseActivity //....

Quan trọng : ngay cả khi bạn mở rộng từ BaseActivity, bạn phải thêm dòng android:configChanges="orientation|screenSize"vào từng dòng Activitytrong AndroidManifest.xml:

    <activity
        android:name=".login.LoginActivity"
        android:configChanges="orientation|screenSize">
    </activity>

5

Chà, điều này hơi muộn nhưng, đây là một giải pháp chỉ dành cho XML nhưng là một giải pháp hack không tái tạo một hoạt động như setRequestedOrientationnếu phải thay đổi hướng:

https://stackoverflow.com/a/27015879/1281930


1
Không hoạt động, máy tính bảng luôn lấy định hướng của điện thoại. Rõ ràng, android: screenOrientation được đặt chính xác một lần cho tất cả các hoạt động trong tệp kê khai, trước khi bất kỳ sửa đổi tài nguyên nào được áp dụng.
0101100101

2

Theo câu trả lời được chấp nhận , tôi đang thêm tệp kotlin với giải pháp hy vọng nó sẽ giúp được ai đó

Đặt tài nguyên bool này vào res/valuesdưới dạng bools.xml hoặc bất cứ điều gì (tên tệp không quan trọng ở đây):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="portrait_only">true</bool>
</resources>

Đặt cái này vào res/values-sw600dpres/values-sw600dp-land:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="portrait_only">false</bool>
</resources>

Sau đó, thêm nó vào các dòng dưới đây trong hoạt động hoặc phân mảnh của bạn

class MyActivity : Activity() {

    @SuppressLint("SourceLockedOrientationActivity")
    override fun onCreate(savedInstanceState: Bundle?) {
        if (resources.getBoolean(R.bool.portrait_only)) {
            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        }
        super.onCreate(savedInstanceState)
    }

    @SuppressLint("SourceLockedOrientationActivity")
    override fun onConfigurationChanged(newConfig: Configuration) {
        if (resources.getBoolean(R.bool.portrait_only)) {
            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        }
        super.onConfigurationChanged(newConfig)
    }
}

1

Các giải pháp khác không hiệu quả với tôi. Tôi vẫn có một số vấn đề định hướng kỳ lạ với các hộp thoại và các vấn đề giải trí. Giải pháp của tôi là mở rộng Hoạt động, buộc nó là chân dung trong bảng kê khai.

Thí dụ:

public class MainActivityPhone extends MainActivity {}

tệp kê khai:

        <activity
        android:screenOrientation="portrait"
        android:name=".MainActivityPhone"
        android:theme="@style/AppTheme.NoActionBar" />

trong hoạt động màn hình:

    Intent i = null;
    boolean isTablet = getResources().getBoolean(R.bool.is_tablet);
    if (!isTablet)
        i = new Intent(this, MainActivityPhone.class);
    else
        i = new Intent(this, MainActivity.class);
    startActivity(i);

0

Câu hỏi cũ tôi biết. Để chạy ứng dụng của bạn luôn ở chế độ dọc ngay cả khi định hướng có thể bị hoán đổi, v.v. các tính năng được tổ chức trên thiết bị.

   private void initActivityScreenOrientPortrait()
    {
        // Avoid screen rotations (use the manifests android:screenOrientation setting)
        // Set this to nosensor or potrait

        // Set window fullscreen
        this.activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        DisplayMetrics metrics = new DisplayMetrics();
        this.activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);

         // Test if it is VISUAL in portrait mode by simply checking it's size
        boolean bIsVisualPortrait = ( metrics.heightPixels >= metrics.widthPixels ); 

        if( !bIsVisualPortrait )
        { 
            // Swap the orientation to match the VISUAL portrait mode
            if( this.activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT )
             { this.activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); }
            else { this.activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ); }
        }
        else { this.activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); }

    }

Hoạt động như một lá bùa!

THÔNG BÁO: Thay đổi this.activitytheo hoạt động của bạn hoặc thêm nó vào hoạt động chính và xóa this.activity;-)

Nếu bạn muốn làm ngược lại, bạn phải thay đổi mã thành ngang (nhưng tôi nghĩ rõ ràng làm thế nào để làm điều này).


0

Thật không may, sử dụng phương thức setRequestedOrientation (...) sẽ khiến hoạt động khởi động lại, vì vậy ngay cả khi bạn gọi phương thức này trong phương thức onCreate, nó sẽ đi qua vòng đời hoạt động và sau đó nó sẽ tạo lại hoạt động tương tự theo hướng được yêu cầu. Vì vậy, tại câu trả lời của @Brian Christensen, bạn nên xem xét rằng mã hoạt động có thể được gọi hai lần, điều này có thể có tác động xấu (không chỉ trực quan, mà còn ở các yêu cầu mạng, phân tích, v.v.).

Hơn nữa, để đặt thuộc tính configChanges trong tệp kê khai theo ý kiến ​​của tôi là một sự đánh đổi lớn, có thể mất chi phí tái cấu trúc lớn. Android Devs không khuyến nghị thay đổi thuộc tính đó .

Cuối cùng, cố gắng thiết lập màn hình khác nhau bằng cách nào đó (để tránh sự cố khởi động lại) là không thể, không thể tĩnh do biểu hiện tĩnh không thể thay đổi, theo lập trình, chỉ có thể gọi phương thức đó trong hoạt động đã bắt đầu.

Tóm tắt: Theo tôi, đề xuất @Brian Christensen là sự đánh đổi tốt nhất, nhưng lưu ý về vấn đề hoạt động khởi động lại.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.