Android: zezwalaj na pionowe i pionowe ustawienie na tabletach, ale wymuszaj portret na telefonie?

191

Chciałbym, aby tablety mogły wyświetlać się w orientacji pionowej i poziomej (w rozdzielczości sw600dp lub większej), ale telefony powinny być ograniczone tylko do portretu. Nie mogę znaleźć żadnego warunkowego wyboru orientacji. Jakieś sugestie?

Kenny Wyland
źródło
Jednym ze sposobów NIE byłoby projektowanie układów poziomych dla telefonów, jak w przypadku korzystania z folderu layout-landwewnętrznego res.
Ghost
12
Spowodowałoby to tylko pokazanie układu pionowego w układzie poziomym. W rzeczywistości nie zapobiegnie to obrotowi telefonu do pozycji poziomej.
radley

Odpowiedzi:

446

Oto dobry sposób na użycie kwalifikatorów zasobów i rozmiaru .

Umieść ten zasób bool w res / wartości jako bools.xml lub cokolwiek innego (nazwy plików nie mają tutaj znaczenia):

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

Umieść to w res / wartości-sw600dp i res / wartości-xlarge:

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

Zobacz tę dodatkową odpowiedź aby uzyskać pomoc dotyczącą dodawania tych katalogów i plików w Android Studio.

Następnie w metodzie onCreate swoich działań możesz to zrobić:

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

Urządzenia o rozdzielczości większej niż 600 dp w kierunku najmniejszej szerokości lub x-large na urządzeniach z systemem Android w wersji wcześniejszej niż 3.2 (zasadniczo tablety) będą zachowywać się normalnie, w oparciu o czujnik i obrót zablokowany przez użytkownika itp . Wszystko inne (telefony, właściwie) będzie tylko portretem.

Brian Christensen
źródło
1
Czy ja też muszę używać, xlargeczy mogę po prostu używać sw600dp? Prawdopodobnie obecnie nie ma tabletów z <3.2.
theblang
7
Cóż, to potencjalnie
wznawia
1
@Bondax Ten komentarz ma zastosowanie tylko „Jeśli aktywność jest obecnie na pierwszym planie lub w inny sposób wpływa na orientację ekranu”, co nie może mieć miejsca, ponieważ w tym momencie jesteśmy w trybie onCreate.
Brian Christensen
1
@BrianChristensen Zauważyłem ponowne uruchamianie aktywności podczas ustawiania żądanej orientacji w onCreate(). Kiedy przeniosłem połączenie na setRequestedOrientation()inną pozycję w czasie i kontekście, restart się nie powtórzył.
Bondax,
8
jeśli uruchomię aplikację w trybie poziomym, przez chwilę nadal będzie wyświetlany tryb poziomy.
最 白 目
29

Możesz spróbować w ten sposób najpierw uzyskać rozmiar ekranu urządzenia

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();
}

a następnie ustaw zgodnie z tym orientację

setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
Avi Kumar Manku
źródło
4
Jaką metodę powinienem umieścić metodę setRequestedOrientation ()? Po uruchomieniu onCreate już wybrał pożądaną orientację.
Kenny Wyland,
kenny sprawdź to, myślę, że może ci to pomóc stackoverflow.com/questions/2833474/...
Avi Kumar Manku
Widzę, że ta odpowiedź została oceniona, ale nie jestem pewien, czy to rozwiązuje problem. Configurationdostarcza screenLayoutinformacje - DPI. Pytanie dotyczy natomiast wielkości ekranu urządzenia fizycznego - telefonu VS tabletu. Oznacza to, że możesz mieć Configuration.SCREENLAYOUT_SIZE_NORMALi byłby to tablet MDPI.
skradziony w nocy
mam problem z stackoverflow.com/questions/42172864/…
Aditya Vyas-Lakhan
10

Uzupełnienie przyjętej odpowiedzi

Możesz wykonać następujące kroki w Android Studio, aby dodać do nich katalogi res/values-sw600dpires/values-largebools.xml plikami.

wartości-sw600dp

Przede wszystkim z zakładki Projekt wybierz filtr Nawigatora (zamiast Androida) w nawigatorze.

wprowadź opis zdjęcia tutaj

Następnie kliknij app/src/main/reskatalog prawym przyciskiem myszy . Wybierz Nowy > Katalog zasobów Androida .

Wybierz opcję Najmniejsza szerokość ekranu , a następnie naciśnij przycisk >> .

wprowadź opis zdjęcia tutaj

Wpisz 600najmniejszą szerokość ekranu. Nazwa katalogu zostanie wygenerowana automatycznie. Powiedz ok.

wprowadź opis zdjęcia tutaj

Następnie kliknij prawym przyciskiem myszy nowo utworzony values-sw600dpplik. Wybierz Nowy > Plik zasobów wartości . Rodzajbools nazwę.

duże wartości

Dodanie values-largekatalogu jest konieczne tylko w przypadku obsługi systemu Android w wersji wcześniejszej niż 3.2 (API poziom 13). W przeciwnym razie możesz pominąć ten krok. values-largeKatalogu odpowiada values-sw600dp. ( values-xlargeodpowiada values-sw720dp.)

Aby utworzyć values-largekatalog, wykonaj te same kroki, co powyżej, ale w tym przypadku wybierz opcję Rozmiar zamiast Najmniejszej szerokości ekranu. Wybierz Large . Nazwa katalogu zostanie wygenerowana automatycznie.

wprowadź opis zdjęcia tutaj

Kliknij katalog prawym przyciskiem myszy, aby utworzyć bools.xmlplik.

Suragch
źródło
2
Daj mi znać, jeśli to nie zadziała, a ja to naprawię. To zadziałało dla mnie. Mam jedną opinię, ale nie mam komentarza, więc nie wiem, co naprawić.
Suragch,
9

Oto jak to zrobiłem (zainspirowany http://androidblogger.blogspot.com/2011/08/orientation-for-both-phones-and-tablets.html ):

W AndroidManifest.xml dla każdej aktywności, którą chcesz mieć możliwość zmiany między portretem a krajobrazem (pamiętaj, aby dodać screenSize - nie było to potrzebne!) Nie musisz tutaj ustawiać orientacji ekranu. :

android:configChanges="keyboardHidden|orientation|screenSize"

Metody dodawania w każdym działaniu:

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

i: (jeśli nie zastąpisz tej metody, aplikacja wywoła funkcję onCreate () podczas zmiany orientacji)

@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
}

W funkcji onCreate () każdego działania:

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

Jedyne, czego nie potrafię zrozumieć, to jak zmusić aplikację do zmiany plików układu podczas przełączania z orientacji poziomej na pionową lub odwrotnie. Zakładam, że odpowiedzią jest zrobienie czegoś podobnego do powyższego linku, ale nie mogłem tego zrobić dla mnie - usunęło to wszystkie moje dane. Ale jeśli masz na tyle prostą aplikację, że masz ten sam plik układu dla portretu i krajobrazu, to powinno działać.

Ginny
źródło
2
umieść układy krajobrazowe w folderze układ-ziemia
radley
Otrzymasz wyjątek, jeśli nie zadzwonisz do super onConfigurationChanged
RominaV
8

Po odpowiedzi Ginny uważam, że najbardziej wiarygodnym sposobem jest:

Jak opisano tutaj , umieść wartość logiczną w zasobach sw600dp. Musi mieć prefiks sw, w przeciwnym razie nie będzie działał poprawnie:

w res / wartości-sw600dp / dimens.xml

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

w res / wartości / dimens.xml

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

Następnie utwórz metodę odzyskania wartości logicznej:

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

I podstawowe działanie, które należy rozszerzyć z działań, w których chcesz tego zachowania:

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

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

Dlatego każde działanie rozszerzy BaseActivity:

public class LoginActivity extends BaseActivity //....

Ważne : nawet jeśli przedłużasz BaseActivity, musisz dodać linię android:configChanges="orientation|screenSize"do każdego Activityz pliku AndroidManifest.xml:

    <activity
        android:name=".login.LoginActivity"
        android:configChanges="orientation|screenSize">
    </activity>
RominaV
źródło
5

Cóż, jest to trochę za późno, ale tutaj jest tylko XML, ale rozwiązanie hakerskie, które nie odtwarza działania tak setRequestedOrientation, jakby miało zmienić orientację:

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

strzelec
źródło
1
Nie działa, tablety zawsze przyjmują orientację telefonów. Najwyraźniej android: screenOrientation jest ustawiany dokładnie raz dla wszystkich działań w manifeście, zanim zostaną zastosowane jakiekolwiek modyfikatory zasobów.
0101100101
2

Po zaakceptowaniu odpowiedzi dodaję plik kotlin z rozwiązaniem, mam nadzieję, że komuś pomoże

Umieścić ten zasób bool w res/valuesjak bools.xml lub cokolwiek (nazwy plików nie ma znaczenia tutaj):

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

Włóż to res/values-sw600dpi res/values-sw600dp-land:

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

Następnie dodaj go poniżej linii w swojej aktywności lub fragmentacji

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)
    }
}
Rahul
źródło
1

Inne rozwiązania nie działały dla mnie. Nadal miałem dziwny problem z orientacją związany z dialogami i problemami z rekreacją. Moim rozwiązaniem było rozszerzenie działania, zmuszając go jako manifest w portrecie.

Przykład:

public class MainActivityPhone extends MainActivity {}

manifest.xml:

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

w działaniu ekranu powitalnego:

    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);
Mengoni
źródło
0

Stare pytanie, które znam. Aby aplikacja działała zawsze w trybie pionowym, nawet gdy orientacja może być lub jest zamieniona itp. (Na przykład na tabletach), zaprojektowałem tę funkcję, która służy do ustawiania urządzenia we właściwej orientacji bez konieczności poznawania orientacji pionowej i poziomej funkcje są zorganizowane na urządzeniu.

   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); }

    }

Działa jak marzenie!

UWAGA: Zmień this.activitywedług swojej aktywności lub dodaj ją do głównej aktywności i usuń this.activity;-)

Jeśli chcesz zrobić odwrotnie, musisz zmienić kod na poziomy (ale myślę, że jest jasne, jak to zrobić).

Codebeat
źródło
0

Niestety przy użyciu metody setRequestedOrientation (...) spowoduje ponowne uruchomienie działania, więc nawet jeśli wywołasz to w metodzie onCreate, przejdzie przez cykl życia działania, a następnie odtworzy tę samą aktywność w żądanej orientacji. Dlatego w odpowiedzi @Brian Christensen powinieneś wziąć pod uwagę, że kod aktywności może zostać wywołany dwukrotnie, co może mieć złe skutki (nie tylko wizualne, ale także na żądania sieciowe, analizy itp.).

Ponadto ustawienie atrybutu configChanges w manifeście jest moim zdaniem dużym kompromisem, który może wymagać ogromnych kosztów refaktoryzacji. Android Devs nie zaleca zmiany tego atrybutu .

Wreszcie próba ustawienia nieco innej orientacji ekranu (aby uniknąć problemu z ponownym uruchomieniem) jest niemożliwa, statycznie niemożliwa z powodu statycznego manifestu, którego nie można zmienić, programowo można wywołać tę metodę tylko w już rozpoczętym działaniu.

Podsumowanie: Moim zdaniem sugestia @Brian Christensen jest najlepszym kompromisem, ale należy pamiętać o problemie z ponownym uruchomieniem.

mathew11
źródło