相信Android 开发者大多数都遇到过横竖屏切换的需求,往往最后选择了锁定竖屏,或者锁定横屏的体验。或者每次切换屏幕就要切换显示数据的控件。谷歌官方推出的RecyclerView控件让你不再有这样的烦恼。
最近这两天又不是很忙了,闲下来看了些关于5.0的一些特性,比如材料设计规范、阴影、主题等等,还有RecyclerView和CardView这两个不错的控件。看到RecyclerView的时候感觉眼前一亮,毫无疑问,它能够完全胜任ListView,GridView以及现在流行的瀑布流(交错布局)显示,并且有着高度解耦,高自由度(自定义分隔线,自定义载入/删除动画等)等特点。
在这里,我用它写了一个横竖屏切换便捷解决方案的Demo。
本demo于API 23 + eclipse环境中完成(但本demo兼容低版本,如下载demo可以修改最低版本)
1.获取RecyclerView的jar包
RecyclerView虽然是谷歌官方推出的控件,但是为了兼容5.0以前的系统版本,它是属于V7兼容包下独立打包的。所以如果我们要使用RecyclerView的话需要导入这个jar包。
更新SDK Manager 中Extras的support library到21+版本后,其jar包在如下位置:
sdk\extras\android\support\v7\recyclerview\libs\android-support-v7-recyclerview.jar
如果不方便更新sdk的话,点击jar包下载地址
导入项目并add to build path。
2.Activity中横竖屏切换控制
OrientationChangeActivity.java
/**
* @author 尘笑ys
* RecyclerView 横竖屏切换demo
*/
public class OrientationChangeActivity extends Activity {
private RecyclerView recyclerView;
private ArrayList<String> datas = new ArrayList<String>();
private LinearLayoutManager mLlayoutmanager;
private GridLayoutManager mGlayoutmanager;
CharAdapter charad;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);
//切换屏幕时保存数据,如有数据,则不再进行数据的初始化
if (savedInstanceState != null && savedInstanceState.getStringArrayList("data") != null) {
datas = savedInstanceState.getStringArrayList("data");
}else {
initData();
}
//竖屏线性展示
mLlayoutmanager = new LinearLayoutManager(this);
//横屏每行显示3个
mGlayoutmanager = new GridLayoutManager(this,3);
reviewonScreenChanged(getResources().getConfiguration());
recyclerView.setAdapter(charad = new CharAdapter(this, datas));
charad.setOnItemClickLitener(new OnItemClickLitener() {
@Override
public void onItemLongClick(View view, int position) {
//这里要先使用这个数据再删除,因为一旦删除,就获取不到这个数据了,获取的就是下一个数据了
Toast.makeText(OrientationChangeActivity.this, datas.get(position)+'\t'+"delete...", Toast.LENGTH_SHORT).show();
charad.removeData(position);
}
@Override
public void onItemClick(View view, int position) {
Toast.makeText(OrientationChangeActivity.this, datas.get(position), Toast.LENGTH_SHORT).show();
}
});
}
private void reviewonScreenChanged(Configuration newConfig) {
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
//横屏
recyclerView.setLayoutManager(mGlayoutmanager);
}else {
//竖屏
recyclerView.setLayoutManager(mLlayoutmanager);
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
reviewonScreenChanged(newConfig);
}
private void initData() {
for (int i = 'A'; i <= 'z'; i++) {
datas.add((char)i + "");
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putStringArrayList("data", datas);
}
}
3.RecyclerView.Adapter
CharAdapter.java
/**
* @author 尘笑ys
*
*/
public class CharAdapter extends RecyclerView.Adapter<CharAdapter.MyViewHolder>{
//类型用自定义的ViewHolder
private List<String> mDatas;
private LayoutInflater mInflater;
//RecyclerView目前需要自己来定义接口
public interface OnItemClickLitener
{
//点击事件
void onItemClick(View view, int position);
//长按事件
void onItemLongClick(View view , int position);
}
private OnItemClickLitener mOnItemClickLitener;
//设置监听的方法,注意参数是CharAdapter.OnItemClickLitener而不是View.OnItemClickLitener
public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
{
this.mOnItemClickLitener = mOnItemClickLitener;
}
//构造时传入数据,如果考虑数据解耦,可将数据赋值放到setData()中
public CharAdapter(Context context, List<String> datas)
{
mInflater = LayoutInflater.from(context);
mDatas = datas;
}
@Override
//视图缓存创建
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
//视图缓存,在ViewHolder构造时获取视图控件
MyViewHolder holder = new MyViewHolder(mInflater.inflate(
R.layout.item_char, parent, false));
return holder;
}
@Override
//视图缓存的数据绑定
public void onBindViewHolder(final MyViewHolder holder, final int position)
{
holder.tv.setText(mDatas.get(position));
// 如果设置了回调,则设置点击事件
if (mOnItemClickLitener != null)
{
holder.itemView.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
int pos = holder.getPosition();
//尽量在回调时处理逻辑,此处不进行处理,增加Adapter复用的可能
mOnItemClickLitener.onItemClick(holder.itemView, pos);
}
});
holder.itemView.setOnLongClickListener(new OnLongClickListener()
{
@Override
public boolean onLongClick(View v)
{
int pos = holder.getPosition();
//尽量在回调时处理逻辑,此处不进行处理,增加Adapter复用的可能
mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
return false;
}
});
}
}
@Override
public int getItemCount()
{
return mDatas.size();
}
//需要自己定义,添加数据后需要调用notifyItemInserted(position),效果类似于ListView的notifyDataSetChanged()
//但是这里会调用预设或者自定义的增加item动画
public void addData(int position)
{
mDatas.add(position, "Insert One");
notifyItemInserted(position);
}
//需要自己定义,添加数据后需要调用notifyItemRemoved(position),效果类似于ListView的notifyDataSetChanged()
//但是这里会调用预设或者自定义的删除item动画
public void removeData(int position)
{
mDatas.remove(position);
notifyItemRemoved(position);
}
//视图缓存
class MyViewHolder extends ViewHolder
{
TextView tv;
//在构造方法中获取控件
public MyViewHolder(View view)
{
super(view);
tv = (TextView) view.findViewById(R.id.id_num);
}
}
}
4.布局
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<android.support.v7.widget.RecyclerView
android:id="@+id/id_recyclerview"
android:divider="#ffff0000"
android:dividerHeight="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
item_char.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/item_bg"
android:layout_margin="2dp"
android:gravity="center"
>
<TextView
android:id="@+id/id_num"
android:layout_width="100dp"
android:layout_gravity="center"
android:layout_height="100dp"
android:gravity="center" />
</RelativeLayout>
5.瀑布流
在Adpater里面修改一下每个item的宽高,RecyclerView的LayoutManager再换成
mLlayoutmanager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
mGlayoutmanager = new StaggeredGridLayoutManager(5,StaggeredGridLayoutManager.HORIZONTAL);
就能看到瀑布流的效果啦。
6.总结与demo下载
这其实是一个十分简单的demo,你可以在此基础上去修改RecyclerView的分隔线以及载入删除动画。
RecyclerView的强大之处就是解耦,以往常常将所有的代码,分隔线、动画、逻辑处理、视图缓存等都放在Adapter中而导致Adapter根本无法复用。相信在大型项目中RecyclerView会有很好的表现以及编程体验。
demo下载
注意:本demo有一处疏漏,需要在Manifest.xml中对对应需要 横竖屏切换 的Activity添加一条属性:
android:configChanges="orientation"