一个实用的android框架(三)—— 兼容性
2015-09-03 12:53
726 查看
原文出处:http://saulmm.github.io/a-useful-stack-on-android-2-user-interface/
原码github地址:https://github.com/saulmm/Material-Movies
作者:Saúl Molinero
系列文章:
一个实用的android框架(一)——架构
一个实用的android框架(二)—— UI
一个实用的android框架(三)—— 兼容性
这是“一个实用的android架构”系列的第三章节。
在第一章节中,我主要讲述了一个模块化和可拓展的架构,这个架构基于Model View Presenter (MVP)。
在第二章节描述在UI上对Material Design的尝试,包括颜色,过渡,矢量图等。
在第三章节中,我们将探讨兼容性的问题。安卓的碎片化是十分严重的,各种各样的版本,屏幕大小,设备特性等等。因为这个原因,我们需要降低应用的版本(原来是Lollipop),并且我们也会尝试适配不同的屏幕大小。
所有的例子都在GihHub上可以找到:https://github.com/saulmm/Material-Movies
为了支持4.1以上的版本,需要多项目做一些必要的修改。例如:共享元素的过渡效果(transitions with shared element)是在5.0才被引入到安卓框架中的。
因为没有办法让一个元素从一个Activity转移到另一个Activity,所以在
效果图:
CircularReveal也是在Lollipop才有的API。为了实现相同的过渡效果,我将视图从点击位置进行了拉伸。
效果图:
问题就在于,我们需要根据屏幕的宽度来决定列数的多少。在GridView中,它有一个属性
Chiu-Ki Chan已经遇到并解决了这个问题,她的解决方法也发布到了博客中。简单的来说,她通过屏幕的宽度来动态的设置spanCount。
效果图:
最佳的实现效果是使用的不同的布局文件,使得实际的
我们可以根据一下几点来对资源文件进行区分:
对于屏幕宽度小于600dp的设备,将使用没有-w600dp的资源文件。这里指的是大部分的收集设备,比如:nexus 5,nexus 4等等。
对于屏幕宽度大于600dp的设备,将被分成3中:-w600dp,-w600dp-land和-w600dp-port。
根据这样一种分类,我们可以将布局文件和Dimen文件放到不同的目录中,以使得应用可以根据不同的屏幕去找到合适的资源。
另外一个问题是
我使用了这个方案来解决适配问题,但是模块化的资源文件提供了跟多的可能解决方案。例如,宽度和高度可以从资源中的dimension获取,在values/dimen.xml中为150dp,在values-v21/dimen.xml中是300dp。这个drawable可能是使用的drawable-v21/star.xml,也可能是drawable/star.xml。
关于这一点,Michal Z.在如何在列表滑动的时候隐藏/显示Toolbar(How to hide/show Toolbar when list is srolling)系列文章中有一个更深入的讲解。
在’-w600dp-land`或更宽尺寸下的布局文件:
这个View是通过ButterKnife注入的。假如是在nexus 5的话,布局文件是目录layouts中的activity_movies.xml,其中并不存在这个View。因此我们必须给这个View标记为@Optional。
结果图:
这个项目的系列博客到这里也就完结。翻译第一篇的时候,是觉得这个项目的架构上做得不错,解释也比较清楚,就翻译了过来。后来看到阅读量比较高,就干脆一口气把后续两篇也翻译了出来,算是有一个整体的介绍。
总得来说,这个项目不论在架构还是UI上都是属于目前比较流行的方向,各个地方都有可取之处。因此大家可以在开发的时候,灵活使用一些代码作为模板,提高自己的开发效率。不过学而不思则罔,在借鉴的时候一定要去思考它的原理以及特性,以使得下次需要遇到类似问题的时候可以自己解决或者重用代码。个人还是觉得,程序员一定要多对自己的代码折腾,不能容许任何啰嗦和重复。只有在不断折腾自己的过程中,才能提高自己的水平。
原码github地址:https://github.com/saulmm/Material-Movies
作者:Saúl Molinero
系列文章:
一个实用的android框架(一)——架构
一个实用的android框架(二)—— UI
一个实用的android框架(三)—— 兼容性
这是“一个实用的android架构”系列的第三章节。
在第一章节中,我主要讲述了一个模块化和可拓展的架构,这个架构基于Model View Presenter (MVP)。
在第二章节描述在UI上对Material Design的尝试,包括颜色,过渡,矢量图等。
在第三章节中,我们将探讨兼容性的问题。安卓的碎片化是十分严重的,各种各样的版本,屏幕大小,设备特性等等。因为这个原因,我们需要降低应用的版本(原来是Lollipop),并且我们也会尝试适配不同的屏幕大小。
所有的例子都在GihHub上可以找到:https://github.com/saulmm/Material-Movies
降低SDK版本
我将SDK的版本定为了16。从Google近日提供的数据可以看到,Jelly Bean(4.1)以上的安卓设备已经占到了86.8%。(这个数据在国内成不成立还未可知)为了支持4.1以上的版本,需要多项目做一些必要的修改。例如:共享元素的过渡效果(transitions with shared element)是在5.0才被引入到安卓框架中的。
共享元素的过渡效果
当你在MoviesActivity中点击一个电影的时候,我们就开始检测当前的版本是不是高于或者等于Lollipop。如果是的话,那么我们就可以使用新的API来完成这个过渡效果。如果不是的话,我使用了一个动画达到了相似的效果。
MoviesActivity
@Override public void onClick(View v, int position, float touchedX, float touchedY) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) startSharedElementPosition(touchedView, position, movieDetailActivityIntent); else startDetailActivityAnimation(touchedView, (int) touchedX, (int) touchedY, movieDetailActivityIntent); }
因为没有办法让一个元素从一个Activity转移到另一个Activity,所以在
MovieDetailActivity中,我会让海报从用户点击的位置开始放大弹出。
MovieDetailActivity.java
@Override public void onCreate(Bundle savedInstanceState) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) configureEnterTransition (); else { mViewLastLocation = getIntent() .getIntArrayExtra("view_location"); configureEnterAnimation (); } } ... private void configureEnterAnimation() { GUIUtils.startScaleAnimationFromPivot( mViewLastLocation[0], mViewLastLocation[1], mObservableScrollView, new AnimatorAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); GUIUtils.showViewByScale(mFabButton); } } ); animateElementsByScale(); }
GuiUtils.java
public static void startScaleAnimationFromPivot ( int pivotX, int pivotY, final View v, final AnimatorListener animatorListener) { final AccelerateDecelerateInterpolator interpolator = new AccelerateDecelerateInterpolator(); v.setScaleY(SCALE_START_ANCHOR); v.setPivotX(pivotX); v.setPivotY(pivotY); v.getViewTreeObserver().addOnPreDrawListener( new OnPreDrawListener() { @Override public boolean onPreDraw() { v.getViewTreeObserver().removeOnPreDrawListener(this); ViewPropertyAnimator viewPropertyAnimator = v.animate() .setInterpolator(interpolator) .scaleY(1) .setDuration(SCALE_DELAY); if (animatorListener != null) viewPropertyAnimator.setListener( animatorListener); viewPropertyAnimator.start(); return true; } }); }
效果图:
VectorDrawables和滑动过渡效果
另一个需要进行适配的是VectorDrawables。它也是在安卓5.0才被引入进来的。为了达到相似的效果,我使用了一个星星旋转并伸缩的动画。这个动画是使用
ViewPropertyAnimator实现的。(译者注:如果想要适配更低的版本,如2.1之类的,请使用NineOldAndroid来代替)
CircularReveal也是在Lollipop才有的API。为了实现相同的过渡效果,我将视图从点击位置进行了拉伸。
MovieDetailActivity.java
@Override public void showConfirmationView() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) GUIUtils.showViewByRevealEffect(mConfirmationContainer, mFabButton, GUIUtils.getWindowWidth(this)); else GUIUtils.startScaleAnimationFromPivot( (int) mFabButton.getX(),(int) mFabButton.getY(), mConfirmationContainer, null); animateConfirmationView(); startClosingConfirmationView(); }
MovieDetailActivity.java
@Override public void animateConfirmationView() { Drawable drawable = mConfirmationView.getDrawable(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) if (drawable instanceof Animatable) ((Animatable) drawable).start(); else mConfirmationView.startAnimation( AnimationUtils.loadAnimation(this, R.anim.appear_rotate)); } }
效果图:
适配不同的屏幕大小
安卓支持不同的设备,设备的大小和屏幕的分辨率都各有不同。为了让一个应用在一个4寸的收集和一个10-12寸的平板上拥有相同的显示效果,一定要使用下面这几个技巧。AutofitRecyclerView
电影是通过RecyclerView和GridLayoutManager来以表格的形式展示的。谷歌提供了一个带有spanCount参数的构造函数,可以用它来设置列数。public GridLayoutManager (Context context, int spanCount)
问题就在于,我们需要根据屏幕的宽度来决定列数的多少。在GridView中,它有一个属性
android:numColumns = "auto_fit"可以解决这个问题。然而,
RecyclerView并没有这个选项。因此,我们想要达到的效果是手动实现类似的效果。
Chiu-Ki Chan已经遇到并解决了这个问题,她的解决方法也发布到了博客中。简单的来说,她通过屏幕的宽度来动态的设置spanCount。
效果图:
多种资源
资源文件在安卓体系中的地位是无可争论的。它可以使得用户在nexus 5上的体验和一个10寸nexus上有很大的区别。MoviesDetailActivity上的元素就可以根据不同的屏幕显示不同的资源。
最佳的实现效果是使用的不同的布局文件,使得实际的
MovieDetailActivity不需要做太大的修改。下面是这个应用的资源目录:
我们可以根据一下几点来对资源文件进行区分:
对于屏幕宽度小于600dp的设备,将使用没有-w600dp的资源文件。这里指的是大部分的收集设备,比如:nexus 5,nexus 4等等。
对于屏幕宽度大于600dp的设备,将被分成3中:-w600dp,-w600dp-land和-w600dp-port。
根据这样一种分类,我们可以将布局文件和Dimen文件放到不同的目录中,以使得应用可以根据不同的屏幕去找到合适的资源。
另外一个问题是
VectorDrawable是在SDK 21之后的版本才被引入到安卓体系中的,因此需要放置到-v21的目录下。例如,使用
VectorDrawable显示星星的
ImageView在Lollipop和较低的版本中是不同的。
activity_detail.xml (通用)
<FrameLayout> <!-- awesome hidden code --> <include layout="@layout/imageview_star" /> </FrameLayout>
imageview_star.xml (layout-v21)
<?xml version="1.0" encoding="utf-8"?> <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_detail_confirmation_image" android:layout_width="300dp" android:layout_height="300dp" android:layout_gravity="center" android:src="@drawable/avd_star" />
imageview_star.xml (layout)
<?xml version="1.0" encoding="utf-8"?> <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_detail_confirmation_image" android:layout_width="150dp" android:layout_height="150dp" android:layout_gravity="center" android:src="@drawable/star" />
我使用了这个方案来解决适配问题,但是模块化的资源文件提供了跟多的可能解决方案。例如,宽度和高度可以从资源中的dimension获取,在values/dimen.xml中为150dp,在values-v21/dimen.xml中是300dp。这个drawable可能是使用的drawable-v21/star.xml,也可能是drawable/star.xml。
视差效果
如果你观察谷歌图书(Google Books)应用,你可以发现在Toolbar下面有一个小的View。当你往上滑动书籍列表的时候,这个View会和
Toolbar有一个滑动速度的差异,从而可以造成视差的效果。
关于这一点,Michal Z.在如何在列表滑动的时候隐藏/显示Toolbar(How to hide/show Toolbar when list is srolling)系列文章中有一个更深入的讲解。
在’-w600dp-land`或更宽尺寸下的布局文件:
<View android:id="@+id/activity_movies_background_view" android:layout_width="match_parent" android:layout_height="@dimen/ activity_movies_background_view_height" android:background="@color/theme_primary" />
这个View是通过ButterKnife注入的。假如是在nexus 5的话,布局文件是目录layouts中的activity_movies.xml,其中并不存在这个View。因此我们必须给这个View标记为@Optional。
MoviesActivity.java
@Optional @InjectView(R.id.activity_movies_background_view) View mTabletBackground;
MoviesActivity.java
private RecyclerView.OnScrollListener recyclerScrollListener = new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { // awesome hidden code here if (mTabletBackground != null) { mBackgroundTranslation = mTabletBackground .getY() - (dy / 2); mTabletBackground.setTranslationY(mBackgroundTranslation); } }
结果图:
译者总结
在这一章节中,主要描述了作者在适配过程中遇到的几个典型问题。可能适用性不是那么广,不过思路还是值得借鉴的。其实适配的过程往往造成了一个很尴尬的情况,即,如果用低版本的代码逻辑可以实现的功能,为什么要用高版本的去实现了,为什么一定要存在两套代码呢?这样子既不利于保证样式统一,也给开发和修改带来了很大的麻烦。这也就是为什么许多旧式的API任然盛行的缘故,API更新推广之路也是相当的缓慢。作为开发人员,能做到的也就是尽量跟紧时代潮流吧。这个项目的系列博客到这里也就完结。翻译第一篇的时候,是觉得这个项目的架构上做得不错,解释也比较清楚,就翻译了过来。后来看到阅读量比较高,就干脆一口气把后续两篇也翻译了出来,算是有一个整体的介绍。
总得来说,这个项目不论在架构还是UI上都是属于目前比较流行的方向,各个地方都有可取之处。因此大家可以在开发的时候,灵活使用一些代码作为模板,提高自己的开发效率。不过学而不思则罔,在借鉴的时候一定要去思考它的原理以及特性,以使得下次需要遇到类似问题的时候可以自己解决或者重用代码。个人还是觉得,程序员一定要多对自己的代码折腾,不能容许任何啰嗦和重复。只有在不断折腾自己的过程中,才能提高自己的水平。
相关文章推荐
- 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路
- Android修改横屏的默认角度
- Android中Service(后台服务)详解
- 结合源代码详解android消息模型
- Android学习笔记_点九绘图与软键盘和事件传递
- Android(java)学习笔记206:利用开源SmartImageView优化网易新闻RSS客户端
- android--仿网易新闻主界面
- 简析android消息模型
- 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
- 【android】 如何把gif图片下载到本地
- 【Java CV与Android】在Android工程里配置JavaCV
- Android进程间通信(IPC)机制Binder简要介绍和学习计划
- 刚开始学习Android遇到的一些问题---主要安卓环境安装Androidstudio以及模拟器测试真机测试
- android颜色设置alpha的问题
- Android Animation动画实战(二):从屏幕底部弹出PopupWindow
- Android Animation动画实战(二):从屏幕底部弹出PopupWindow
- Android(java)学习笔记205:网易新闻RSS客户端应用编写逻辑过程
- Android本地服务写法 及 生成脚本
- 使用FragmentTabhost代替Tabhost onCreatView 每次都被调用
- 7.1Android服务绑定