Android-屏幕适配全攻略(绝对详细)(二)
来源:互联网 发布:立花瑠莉 知乎 编辑:程序博客网 时间:2024/05/22 15:55
Android-屏幕适配全攻略(绝对详细)(二)
关键字:非密度制约像素(dp、sp)
屏幕适配最佳实践
前言:这篇文章接着 上一篇 继续讲,上篇讲到了屏幕适配的核心概念与单位和一种解决方案:解决方案-支持各种屏幕尺寸。本篇继续讲剩余的其他两种解决方案。
三. 解决方案-支持各种屏幕密度
- 解决屏幕宽度不一致问题
思路:把任何设备的手机宽度像素均分为320份,高度像素均分为480份,使用我们写好的程序自动生成资源values-***×***
文件夹,里面包含lay_x.xml
和lay_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; }}
执行上面程序,会生成下面这些文件夹和文件:
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,以此类推。
因此,我们要在drawable
、drawable-hdpi
、drawable-mdpi
、drawable-xdpi
、drawable-xhdpi
等文件夹下放置相同名称、符合上述比例的图片资源。系统会根据屏幕密度的不同,而选择对应的图片进行加载。
在布局文件中的简单使用:
<Button android:layout_height="wrap_content" android:layout_width="wrap_content" android:background="@drawable/ic_launcher" />
小插曲:
如果我们只提供一个图片来适配不同屏幕密度的设备的话,就要考虑放在哪个文件夹下了。
我们以Nexus5为例,如果我们把图片放在drawable-xxhdpi
下,占用的内存最小(凯子哥的例子是11.65M),如果放在drawable
或drawable-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); }}
- Android-屏幕适配全攻略(绝对详细)(二)
- Android-屏幕适配全攻略(绝对详细)(二)
- Android-屏幕适配全攻略(绝对详细)(二)
- Android-屏幕适配全攻略(绝对详细)(二)
- Android-屏幕适配全攻略(绝对详细)
- Android-屏幕适配全攻略(绝对详细)
- Android-屏幕适配全攻略(绝对详细)(一)
- Android-屏幕适配全攻略(绝对详细)(一)
- Android-屏幕适配全攻略(绝对详细)(一)
- Android-屏幕适配全攻略(绝对详细
- 屏幕适配全攻略(二)-- 解决方案
- Android屏幕适配全攻略
- Android屏幕适配全攻略
- Android屏幕适配全攻略
- Android屏幕适配全攻略
- Android屏幕适配全攻略
- Android-屏幕适配全攻略
- Android屏幕适配全攻略
- 第十二周 项目四 图的深度遍历和广度遍历
- Kaggle大数据竞赛平台入门
- 图的遍历
- maven更换远程仓库
- 连续输入密码错误3次,第二天才能登录
- Android-屏幕适配全攻略(绝对详细)(二)
- 区块链四级知识考试
- VR世界 - 实战起步指南
- 常用ProgressDiaolog
- where 1=1影响效率以及having和where的区别
- JAVA3D
- 协调作用域不同步的Bean
- Android实现序列化
- 史上最详细的iOS之事件的传递和响应机制