Android-屏幕适配全攻略(绝对详细)(二)

来源:互联网 发布:立花瑠莉 知乎 编辑:程序博客网 时间:2024/05/22 15:55

Android-屏幕适配全攻略(绝对详细)(二)

关键字:非密度制约像素(dp、sp) 屏幕适配最佳实践

前言:这篇文章接着 上一篇 继续讲,上篇讲到了屏幕适配的核心概念与单位和一种解决方案:解决方案-支持各种屏幕尺寸。本篇继续讲剩余的其他两种解决方案。

三. 解决方案-支持各种屏幕密度

  • 解决屏幕宽度不一致问题
    思路:把任何设备的手机宽度像素均分为320份,高度像素均分为480份,使用我们写好的程序自动生成资源values-***×***文件夹,里面包含lay_x.xmllay_y.xml,分别对应宽度和高度的像素。

程序代码如下:

public class MakeXml {    private final static String rootPath = "F:\\layoutroot\\values-{0}x{1}\\";    private final static float dw = 320f;    private final static float dh = 480f;    private final static String WTemplate = "<dimen name=\"x{0}\">{1}px</dimen>\n";    private final static String HTemplate = "<dimen name=\"y{0}\">{1}px</dimen>\n";    public static void main(String[] args) {        makeString(320, 480);        makeString(480, 800);        makeString(480, 854);        makeString(540, 960);        makeString(600, 1024);        makeString(720, 1184);        makeString(720, 1196);        makeString(720, 1280);        makeString(768, 1024);        makeString(800, 1280);        makeString(1080, 1812);        makeString(1080, 1920);        makeString(1440, 2560);    }    public static void makeString(int w, int h) {        StringBuffer sb = new StringBuffer();        sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");        sb.append("<resources>");        float cellw = w / dw;        for (int i = 1; i < 320; i++) {            sb.append(WTemplate.replace("{0}", i + "").replace("{1}",                    change(cellw * i) + ""));        }        sb.append(WTemplate.replace("{0}", "320").replace("{1}", w + ""));        sb.append("</resources>");        StringBuffer sb2 = new StringBuffer();        sb2.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");        sb2.append("<resources>");        float cellh = h / dh;        for (int i = 1; i < 480; i++) {            sb2.append(HTemplate.replace("{0}", i + "").replace("{1}",                    change(cellh * i) + ""));        }        sb2.append(HTemplate.replace("{0}", "480").replace("{1}", h + ""));        sb2.append("</resources>");        String path = rootPath.replace("{0}", h + "").replace("{1}", w + "");        File rootFile = new File(path);        if (!rootFile.exists()) {            rootFile.mkdirs();        }        File layxFile = new File(path + "lay_x.xml");        File layyFile = new File(path + "lay_y.xml");        try {            PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile));            pw.print(sb.toString());            pw.close();            pw = new PrintWriter(new FileOutputStream(layyFile));            pw.print(sb2.toString());            pw.close();        } catch (FileNotFoundException e) {            e.printStackTrace();        }    }    public static float change(float a) {        int temp = (int) (a * 100);        return temp / 100f;    }}

执行上面程序,会生成下面这些文件夹和文件:


生成的一系列文件夹

每个文件夹下的xml文件

F:\layoutroot\values-480x320\lay_x.xml

<?xml version="1.0" encoding="utf-8"?><resources><dimen name="x1">1.0px</dimen><dimen name="x2">2.0px</dimen><dimen name="x3">3.0px</dimen><dimen name="x4">4.0px</dimen><dimen name="x5">5.0px</dimen>...省略...<dimen name="x316">316.0px</dimen><dimen name="x317">317.0px</dimen><dimen name="x318">318.0px</dimen><dimen name="x319">319.0px</dimen><dimen name="x320">320px</dimen></resources>

F:\layoutroot\values-480x320\lay_y.xml

<?xml version="1.0" encoding="utf-8"?><resources><dimen name="y1">1.0px</dimen><dimen name="y2">2.0px</dimen><dimen name="y3">3.0px</dimen><dimen name="y4">4.0px</dimen><dimen name="y5">5.0px</dimen>...省略...<dimen name="y476">476.0px</dimen><dimen name="y477">477.0px</dimen><dimen name="y478">478.0px</dimen><dimen name="y479">479.0px</dimen><dimen name="y480">480px</dimen></resources>

在480×320的设备上,x2就代表2.0px,y2就代表3.0px。
在800×480的设备上,x2就代表3.0px,y2就代表3.33px。依次类推。

如何使用:

<Button        android:background="#abd123"        android:layout_width="@dimen/x160"        android:layout_height="@dimen/y240"        android:text="Button" />

这样设置,在各种屏幕宽度的设备上,此Button的宽度和高度就都占屏幕的一半。

效果如下:


各种屏幕密度

我们看到这种方式可以支持大部分屏幕宽度的设备,但是我们也看到了一些设备,如Nexus9、Nexus10上并没有显示出Button,这是因为我们生成的尺寸资源文件里没有对应分辨率的xml文件。

  • 提供备用位图
    为了让我们提供的图片符合各种屏幕密度的要求。我们需要为不同屏幕密度提供大小不同的图片。
    上篇文章中我们提到了

    在Google官方开发文档中,说明了 mdpi:hdpi:xhdpi:xxhdpi:xxxhdpi=2:3:4:6:8 的尺寸比例进行缩放。例如,一个图标的大小为48×48dp,表示在mdpi上,实际大小为48×48px,在hdpi像素密度上,实际尺寸为mdpi上的1.5倍,即72×72px,以此类推。

因此,我们要在drawabledrawable-hdpidrawable-mdpidrawable-xdpidrawable-xhdpi等文件夹下放置相同名称、符合上述比例的图片资源。系统会根据屏幕密度的不同,而选择对应的图片进行加载。

在布局文件中的简单使用:

 <Button        android:layout_height="wrap_content"        android:layout_width="wrap_content"        android:background="@drawable/ic_launcher" />

小插曲:
如果我们只提供一个图片来适配不同屏幕密度的设备的话,就要考虑放在哪个文件夹下了。

我们以Nexus5为例,如果我们把图片放在drawable-xxhdpi下,占用的内存最小(凯子哥的例子是11.65M),如果放在drawabledrawable-mdpi下,占用的内存将会非常大(凯子哥的例子是74.95M)。如果放在drawable-hdpi下占用的为35.38M(同一张图片),所以,我们要提供不同尺寸的图片来适配不同的屏幕密度,否则可能会很大程度上浪费内存。

四. 解决方案-实施自适应用户界面流程

我们如果为手机和平板设备适配了不同的布局,如我们使用的单面板和双面板,这样就导致了用户操作流程的不同。所以,我们必须做出一些必须的判断来适应用户界面流程。

  • 确定当前布局
    要确定当前设备使用的布局,可以通过布局是否显示出来做出判断。

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

    在对某些组件执行操作前先查看它们是否可用,比如菜单按钮,在api11以上用actionbar中的按钮代替了。

    Button catButton = (Button) findViewById(R.id.categorybutton);OnClickListener listener = /* create your listener here */;if (catButton != null) {  catButton.setOnClickListener(listener);}
  • 根据当前布局做出响应

例如,在单面板模式下,用户点击了新闻标题,我们要打开一个新的activity来显示新闻详细信息;在双面板模式下,用户点击了新闻标题,我们要在右边面板上显示详细信息。

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

同样,如果该应用处于双面板模式下,就应设置带导航标签的操作栏;但如果该应用处于单面板模式下,就应使用下拉菜单设置导航栏。因此我们的代码还应确定哪种情况比较合适:

final String CATEGORIES[] = { "热门报道", "政治", "经济", "Technology" };public void 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 = new ArrayAdapter(this,                R.layout.headline_item, CATEGORIES);        actionBar.setListNavigationCallbacks(adap, handler);    }}
0 0
原创粉丝点击