关于Fragment的使用与多屏幕的设计

从Android3.0开始,Android引入了Fragment,使得界面能够进行很好的复用;

Fragment是依附于Activity存在的,单独的Fragment并不能单独显示,而是需要在Activity的布局中进行显示;

Fragment的用法:

1、直接在Activity对应布局文件中指定相应的Fragment;

2、在代码中动态的为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>

第二种用法:

 if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) {
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    MyCloudFragment fragment = new MyCloudFragment();
    transaction.add(fragment, FRAGTAG);
    transaction.commit();
}

或者

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
AdapterTransitionFragment fragment = new AdapterTransitionFragment();
transaction.replace(R.id.sample_content_fragment, fragment);
transaction.commit();

前面一种在是布局中增加Fragment,后者是在布局中,使用Fragment替换FrameLayout ;

Fragment重在复用,下面我们看一下如何设计适应多种屏幕大小的app界面

设计适应多种屏幕大小的app
 
1. 使用 wrap_content 和 match_parent
为了保证你的布局是灵活的而且能够适应不同的屏幕大小的;对于某些视图组件应该使用 wrap_content或者match_parent来适应宽度和高度;
wrap_content使得视图在高度或者宽度上使用尽可能少的大小来适应内容;
fill_parent 则会填充到父视图的长度/宽度;
 
2. 另外一种方式,是使用最小宽度尺寸过滤器(smallest-width),根据不同的屏幕大小,使用不同的layout;例如下面两个layout
 
res/layout/main.xml   普通版手机布局
 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="match_parent" />
</LinearLayout>
 
res/layout-sw600dp/main.xml  平板/TV布局(最小屏幕宽度大于600dp的话,会选择这个布局)
 
<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>
 
但是在安卓3.2之前的版本,并不能区分 sw600dp 这种过滤器,而最好使用 layout_large 这种方式;
但是这样的话,可能会造成两个文件是一模一样的,这样就不好维护,为了解决这个问题,可以使用别的方式
在layout中,创建一个main_twopanes.xml的这个文件,就是之前的 res/layout-sw600dp/main.xml  这个文件
然后,在 layout-large/main.xml和layout-sw600dp/main.xml都是一样的
<resources>
    <item name="main" type="layout">@layout/main_twopanes</item>
</resources>
 
这样就使得布局文件更好维护,只需要修改一个文件就行了;
 
3. 使用横向纵向布局过滤
res/values/layouts.xml                                          普通版
res/values-sw600dp-land/layouts.xml                 最小尺寸600dp横向版
res/values-sw600dp-port/layouts.xml                 最小尺寸600dp纵向版
res/values-large-land/layouts.xml                       大屏横向版
res/values-large-port/layouts.xml                        大屏纵向版
 
4. 使用可拉伸的图标
在一些场景下,同一个图片再经过了横向,纵向的变化后;或者在不同的屏幕尺寸下,需要展示的大小会不一样,这个时候,使用一个可拉伸的图片就很重要了
首先准备一个普通的PNG图片,然后使用SDK下的tools/draw9patch.bat 工具,来选择拉伸区域;
 
 
支持不同的屏幕密度(分辨率)
使用密度无关的像素
 
在布局文件里,dp和sp就是这种与pixels无关的单位,他们都是由系统来去自适应相对的宽度的;dp是针对布局的,sp针对的是文字的大小; 
在布局文件中,应该使用dp/sp,而不要使用px像素这个单位;
 
提供不同的可选Bitmap
有以下几种初始格式
xhdpi: 2.0
hdpi: 1.5
mdpi: 1.0 (baseline)
ldpi: 0.75 
 
200*200 , 150*150 ,100*100 , 75*75
 
在res结构中,需要组织成下面这样,不同的目录,使用对应尺寸的图片
 
MyProject/
  res/
    drawable-xhdpi/
        awesomeimage.png
    drawable-hdpi/
        awesomeimage.png
    drawable-mdpi/
        awesomeimage.png
    drawable-ldpi/
        awesomeimage.png
 
 
实现自适应的UI流程
例如,阅读文章的APP的例子,两个视图时候,一个选择文章之后,第二个进行展示;而如果是小屏幕只有一个视图的时候,则显示到另外一个Activity上;
这就是不同的UI流程;
 
 
下面是一种判断是是单屏还是双屏显示的方法
 
public class NewsReaderActivity extends FragmentActivity {
    boolean mIsDualPane;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);
 
        View articleView = findViewById(R.id.article);
        mIsDualPane = articleView != null &&
                        articleView.getVisibility() == View.VISIBLE;
    }
}
 
一种好的方式是,针对每个视图的操作,都判断其存不存在,因为有可能不在屏幕上显示;
如下
Button catButton = (Button) findViewById(R.id.categorybutton);
OnClickListener listener = /* create your listener here */;
if (catButton != null) {
    catButton.setOnClickListener(listener);
}
 
根据不同的方式,进行不同的流程,如下面是选择一篇文章标题时候的处理流程
 
@Override
public void 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 = new Intent(this, ArticleActivity.class);
        intent.putExtra("catIndex", mCatIndex);
        intent.putExtra("artIndex", index);
        startActivity(intent);
    }
}
 
复用Fragment
 
在双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>
 
在单Fragment的情况下,在ArticleActivity中,可以使用以下代码进行复用
 
ArticleFragment frag = new ArticleFragment();
getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();
 
android.R.id.content 是对应的Fragment的布局文件id;
 
需要注意的是,不要设计耦合程度太高的Activity,这样会使得不好维护;例如应该使用下面的方式进行设计选择文章的操作;
 
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;
    }
 
    @Override
    public void onItemClick(AdapterView<?> parent,
                            View view, int position, long id) {
        if (null != mHeadlineSelectedListener) {
            mHeadlineSelectedListener.onHeadlineSelected(position);
        }
    }
}
 
使用观察者模式,是比较好的解耦方式;
 
而需要注意的是,在一些横向纵向模式下需要做转换,从单片格到多片格的这种方式的话,由于重新进行渲染,这个时候,我们需要根据情况来进行处理;
例如下面的代码,就是根据在纵向变成横向之后,单片变多片,需要将文章的Activity给结束掉,返回主Activity去;
 
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;
 }
 ...
}

以上文章的内容,大部分是《Android.4.2.documentation-1.6.chm》这个android文档里面的内容,主要是经过了自己的粗略翻译和增加了部分个人理解,水平有限,难免有错误的地方,还望指正;

原文地址:https://www.cnblogs.com/coldforce/p/4993429.html