2013-03-06 1 views
1

내가 사방 검색했습니다 전혀이 일을 어떤 방식으로 찾을 수 없습니다 : 하나는 "레이아웃"이라고하며, 다른 하나는 "레이아웃이라고 :런타임에 가로 버전의 레이아웃 존재를 감지하는 방법은 무엇입니까?

을 내 프로젝트에서를, 나는 레이아웃에 두 개의 자원 폴더가 -땅". 이것은 현재 방향에 따라 사용되는 각 Activity에 대해 두 개의 개별 레이아웃을 갖는 친숙한 방법입니다. 문제는 모든 액티비티에 대해 별도의 "레이아웃 - 랜드"레이아웃이 없으며 모든 액티비티가 가로 모드의 다른 레이아웃을 필요로하지 않는다는 것입니다.

내가 수행 한 작업은 방향 변경이 발생하지 않도록 기본값 onConfigurationChanged을 재정의합니다 (해당 configChanges="orientation"을 매니페스트에 사용). 이것은 사용자가 화면을 기울일 때마다 번들로 물건을 채우는 두통을 덜어 줬습니다. 그러나 경우에 따라 실제로는 방향 변경을 원합니다. "layout-land"폴더에 해당 레이아웃이있을 때마다 평소와 같이 방향이 바뀌길 원합니다. 없을 때마다 onConfigurationChanged 방향 변경 이벤트를 억제하고 싶습니다.

나는 Activity를 확장하고 내 모든 활동의 기본 클래스 인 부모 BaseActivity 클래스를 만들고, onConfigurationChanged 오버라이드 :

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

    if(...there is a layout-land version of the current activity's layout...) 
    { 
     setRequestedOrientation(newConfig.orientation); 
    } 
    else 
    { 
     // Do nothing, since raising the orientation change event 
     // to change from a portrait layout to the same portrait layout is silly. 
    } 
} 

만하다가 내가 여부를 결정하는 어떤 방법을 찾을 수 있다는 것입니다 내 레이아웃 리소스의 레이아웃 랜드 버전이 있습니다. 이미 (setContentView을 무시하여) 내 BaseActivity 클래스의 다른 위치에서 얻은 현재 레이아웃의 리소스 ID를 알고 있지만 레이아웃의 가로 버전과 세로 버전 모두 동일한 ID를 공유하고 있습니다. Resources 개체를위한 쉬운 방법은 없습니다. ID로 특정 리소스를 요청할 때 어떤 버전의 레이아웃을 말해 주는지 알려주십시오. 이것을 할 수있는 방법이 있어야합니다. 내가 뭘 놓치고 있니?

+0

상태를 저장하지 않아도되도록'onConfigurationChanged()'를 재정의하는 것은 나쁜 습관 일뿐입니다. 나중에 다시 물으려고합니다. – kcoppock

+0

'onSaveInstanceState'의 전체 존재는 나쁜 습관입니다. 사용자가 전화를 돌렸을 때만 응용 프로그램을 완전히 폐기 할 준비가되어 있어야한다는 생각은 잘못된 디자인입니다. 또한 _how_는 상태를 저장하지 않으므로 다시 물지 않습니다. 이 작업을 수행하는 유일한 이유는 사용자가 화면을 기울이는 정확한 시간에 대화 상자가 나타날 수있는 상황을 피하기 위해서입니다. 사용자 데이터는 변경 될 때마다 (필요할 경우 'onSaveInstanceState'포함) 내 앱에 저장됩니다. – Alex

+0

앱이 실행 중이며 사용자가 알림을 열기로 결정했다고 가정 해 보겠습니다. 앱이 백그라운드에 있습니다. 메모리 사용량에 따라 사용자가 완료되기 전에 앱이 중지되거나 삭제 될 수 있습니다 (적어도 중단됩니다). 사용자가 "뒤로"를 클릭하여 활동으로 돌아 오면 활동이 다시 작성되고 해당 활동 인스턴스에 포함 된 모든 데이터는 사라집니다. – kcoppock

답변

1

좋아요, 그래서 비참하고 해킹 된 솔루션이긴하지만 해결책을 찾았습니다.

활동의 getResources() 메서드에서 얻을 수있는 객체는 "레이아웃 - 랜드"버전의 리소스가 있는지 알려주지 않습니다. 그러나 메서드를 호출하여 Resources 개체가 내부적으로 사용하는 AssetManager 개체를 얻을 수 있습니다.

AssetManager 개체에는 openNonAssetFd이라는 메서드가있어서 res 폴더의 특정 파일에 대한 파일 설명자를 가져 오려고 시도합니다. 불행하게도,이 메소드로 열려고 시도하는 대부분의 (전부는 아닐지라도) 파일이 존재하더라도, FileNotFoundException을 던질 것입니다! 당신이 res/layout-land/settings_menu.xml라는 파일이있는 경우

그래서, 예를 들어, 다음 메시지와 함께 FileNotFoundException가 발생합니다 : "이 파일은 파일 기술자로 열 수 없습니다, 그것은 아마 압축". 기술적 인 측면에서 볼 때, 안드로이드 팀의 디자인은 좋지 않습니다. 파일이 있고, 던져진 예외는 적어도 다른 유형이어야합니다.

그러나 더 중요한 것은, 레이아웃의 단순한 존재를 감지하기 위해서, 던져진 예외에 대한 리턴 메시지의 차이에 의존 할 수 있습니다. 여기에는 해킹이 있습니다.

파일이 존재하면 "이 파일을 파일 설명자로 열 수 없으며 압축되었을 가능성이 있습니다"라는 예외 메시지가 표시됩니다.

파일이 존재하지 않는 경우 예외 메시지는 openNonAssetFd 메서드에 전달한 매개 변수와 동일합니다 (즉, 존재하지 않는 리소스의 파일 이름이됩니다).

자,이와, 우리가 같은 방법을 만들 수 있습니다은 "레이아웃 땅"폴더에 현재 활동의 레이아웃의 버전이 있다면이 방법을 사용

protected boolean landscapeLayoutExists(int layoutId) 
{ 
// Since we're always passing in an id of a layout, this will always correspond 
// to a layout file in any of the layout folders (either layout, or both layout and layout-land, in my case). 
String rawName = this.getResources().getResourceEntryName(layoutId); 
String testPath = "res/layout-land/" + rawName + ".xml"; 

try 
{ 
    this.getResources().getAssets().openNonAssetFd(testPath); 
} 
catch(Exception ex) 
{ 
    if(ex.getMessage().equals(testPath)) 
     return false; 
    else 
     return true; 
} 

// Somehow, we managed to grab the file descriptor successfully. 
// In my experience, this cannot happen with layouts, but if it ever does, 
// then we definitely know the file exists. 
return true; 
} 

을, 지금 확인할 수 있습니다 그에 따라 행동하십시오.

그러나이 방법은 작동하지 않지만 만족 스럽습니다 (적어도 지금은 Galaxy S3에서 테스트했습니다). 누구든지 더 나은 사람이 있으면 알려주세요.

1

layout_land 폴더의 레이아웃에서 가장 바깥 쪽 FrameLayout에 id = layout_name_of_this_file을 추가하십시오. 예를

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/layout_name_of_this_file" 
    ..... 

및 코드에서

getResources().getIdentifier("layout_name_of_this_file", "id", "com.example....")) = resource id of the button (a pretty long number) 

getResources().getIdentifier("layout_name_of_the_layout_file_exist_in_layout_but_not_in_layout_land", "id", "com.example....")) = 0 

이 ID를 가진 더 FrameLayout이 없기 때문에

.

+0

글쎄, 처음에는'getIdentifier'가 단순히'R' 객체로 컴파일 된'id' 정수를 리턴하기 때문에 작동하지 않을 것입니다. 해당 'button_fake_activity_name' id를 만든 경우 현재 프리 레이아웃에 있는지 여부에 관계없이 존재합니다 (리소스는 활동별로가 아니라 애플리케이션 당 존재합니다). 그러나 오리엔테이션을 바꾸고 싶지 않은 활동에서'onConfigurationChanged'를 단지 쉽게 오버라이드 할 수 있습니다. 문제는 각 액티비티 대신에'BaseActivity' 클래스에 넣을 수있는 간단한 솔루션이 필요하다는 것입니다. (나는 50+ 활동을 가지고 있습니다 ...) – Alex

+0

이름 활동을위한 layout_land가 없다면 button_fake_activity_name이없고 위의 코드는 0을 반환합니다. 내 테스트 앱에서 테스트했습니다. –

+0

나는 가짜 버튼을 layout_land에만 넣고 일반 레이아웃에는 넣지 않는다는 것을 의미합니다. 따라서 layout_land 버튼이 없습니다. –

관련 문제