您的位置:首页 > 产品设计 > UI/UE

实现自适应的UI界面

2015-08-29 15:59 447 查看
  根据 app 当前的所显示的 layout,UI 界面往往会有差异。如在 dual-pane 模式下,点击左侧的 item,会直接在右侧显示相应的内容;但是如果是在 single-pane 模式下,那么内容将会被显示到另外的界面上(通常会显示在另外的 activity 上)。

  一、那么如果决定当前要采用怎样的 layout 呢?
  也许你需要的所有 layout 之间都是有差异的,但是不管你要实现哪种 layout,你首先要考虑的是用户当前支持哪种形式的 layout。如你必须知道用户是支持的是 single-pane 还是 dual-pane 模式,这你可以通过检测特定 view 的可见性来做到:

public class NewsReaderActivity extends FragmentActivity{
    boolean mIsDualPane;

    @Override
    publicvoid onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);

        View articleView = findViewById(R.id.article);
        mIsDualPane = articleView !=null&&
                        articleView.getVisibility()==View.VISIBLE;
    }
}


上面的这段代码通过检测 "article" pane 的可见性来判断用户是否支持 dual-pane。
  
  此外,在你要对某种组件执行相关操作的时候,你还需要判断它是否存在。如在 News Reader app 中,如果系统版本是 Android 3.0 以下的版本,那么它将通过一个按钮来打开下拉菜单,而在 Android 3.0 以后,打开下拉菜单的功能可由 ActionBar 来实现。所以在给 button 添加 listener 的时候,你可以这样做:

Button catButton =(Button) findViewById(R.id.categorybutton);
OnClickListener listener =/* create your listener here */;
if(catButton !=null){
    catButton.setOnClickListener(listener);
}


二、根据当前的 Layout 处理用户操作
  根据 layout 的不同,对于同一事件的响应方式可能是不同的。如大 News Reader app 中,点击左侧的 headline 列表时,如果当前设备支持 dual mode,那么相应 item 的内容将会被显示在右侧。而如果是 single mode,那么其内容将会在新的 activity 中显示。

@Override
publicvoid onHeadlineSelected(int index){
    mArtIndex = index;
    if(mIsDualPane){
        /* display article on the right pane */
        mArticleFragment.displayArticle(mCurrentCat.getArticle(index));
    }else{
        /* start a separate activity */
        Intent intent =newIntent(this,ArticleActivity.class);
        intent.putExtra("catIndex", mCatIndex);
        intent.putExtra("artIndex", index);
        startActivity(intent);
    }
}


  类似的,如果设备支持 dual-pane,那么 action bar 就可以用 tabs 来实现 navigation,否则 action bar 就应该通过 spinner widget 来实现 navigation。所以你要检测当前设备适合采用哪种方式:

finalString CATEGORIES[]={"Top Stories","Politics","Economy","Technology"};

publicvoid onCreate(Bundle savedInstanceState){
    ....
    if(mIsDualPane){
        /* use tabs for navigation */
        actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
        int i;
        for(i =0; i < CATEGORIES.length; i++){
            actionBar.addTab(actionBar.newTab().setText(
                CATEGORIES[i]).setTabListener(handler));
        }
        actionBar.setSelectedNavigationItem(selTab);
    }
    else{
        /* use list navigation (spinner) */
        actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_LIST);
        SpinnerAdapter adap =newArrayAdapter(this,
                R.layout.headline_item, CATEGORIES);
        actionBar.setListNavigationCallbacks(adap, handler);
    }
}


三、在其它 activity 中重用 Fragments
  在不同 configurations 的屏幕中,界面的可重用性是非常重要的。在 News Reader app 中,如果设备的是大屏幕的,那么它就可以直接在右侧显示 item 的内容,否则它就会通过启动另外的 activity 来显示。像这种情况,你可以通过在 activity 中重用 Fragment 来避免写重复的代码。下面这段代码是用于在大屏幕设备上布局的

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="400dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent"/>
</LinearLayout>


  而如果换到小屏幕设备,你就可以在新的 activity(ArticleActivity) 中重用已经写好的 Fragment

ArticleFragment frag =new ArticleFragment();
getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();


  显然,这和直接在 xml 中声明 fragment 的效果是一样的,但是这样写你就不需要在 xml 中声明了,因为这个 fragment 就是该 activity 的所需要的所有内容了。

  在设计 fragment 的时候,一个非常重要的原则是:不要让任何 fragment 和 activity 有大多的耦合。如果 activity 需要和 fragment 交互,那么你可以在 fragment 中声明接口,就像在 News Reader app 中的实现一样:首先在 HeadlinesFragment 中声明接口

public class HeadlinesFragment extends ListFragment{
    ...
    OnHeadlineSelectedListener mHeadlineSelectedListener = null;

    /* Must be implemented by host activity */
    public interface OnHeadlineSelectedListener{
        public void onHeadlineSelected(int index);
    }
    ...

    public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener listener){
        mHeadlineSelectedListener = listener;
    }
}


  这样当用户点击 headline 的时候,fragment 就可以通知通过 host activity 传递给它的 listener 回调相应的响应方法 

public class HeadlinesFragment extends ListFragment{
    ...
    @Override
    publicvoid onItemClick(AdapterView<?> parent,
                            View view,int position,long id){
        if(null!= mHeadlineSelectedListener){
            mHeadlineSelectedListener.onHeadlineSelected(position);
        }
    }
    ...
}


四、处理 Screen Configuration Changes 事件
  如果你在用不同的 activity 来实现不同的界面,那么你就得处理好 configuration changes 事件,以保持界面的一致性。如在系统版本大于等  Android 3.0 的 tablet 上,在竖屏的时候, article 会在新的 activity 中显示,而切换到横屏之后,左侧就显示文章列表,而右侧显示文章内容。所以你要检测当前设备屏幕是处于 portrait
还是 landscape 模式,然后做相应处理。如用户从 portrait 切换到 landscape,那么就应该把 content activity finish 掉,然后回到 main activity(因为此时 main activity 已经可以显示 content 了)

public class ArticleActivity extends FragmentActivity{
    int mCatIndex, mArtIndex;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        mCatIndex = getIntent().getExtras().getInt("catIndex",0);
        mArtIndex = getIntent().getExtras().getInt("artIndex",0);

        // If should be in two-pane mode, finish to return to main activity
        if(getResources().getBoolean(R.bool.has_two_panes)){
            finish();
            return;
        }
        ...
}


下载 News Reader 源码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息