Android5.x 新控制元件之RecyclerView,CardView,Palette的使用
自Android5.0釋出以來,谷歌推出全新的Material Desigen設計風格,時過一年多了,在國內也看到很多應用在慢
慢適應MD設計風格。其中比較好的app就是網易新聞客戶端了,其設計風格基本符合MD要求。鑑於越來多App採
用MD設計風格,作為吊絲程式設計師的我們怎能落後呢?那就讓我們來學習一些Android5.x新推出的一些控制元件吧。
先上效果圖:
註明:我的開發環境是AS1.0 ,使用Eclipse的童鞋自行配置。為了能使用Android5.0的一些新控制元件,我們不
得不引用Support v7包,在AS裡很好配置,直接在build.gradle檔案下新增如下配置:
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:recyclerview-v7:21.0.3'
compile 'com.android.support:cardview-v7:21.0.3'
compile 'com.android.support:palette-v7:22.2.0'
}
RecyclerView控制元件
類似ListView ,GridView,RecyclerView也是一個繼承ViewGroup的容器控制元件,用於在有限的介面檢視情況下裝
載更多的內容。我們通過程式碼來看看RecyclerView控制元件怎麼使用的吧!先XML從佈局看
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
很簡單,直接引用Support v7包裡的RecyclerView就可以了,貌似佈局檔案裡沒有太多的配置,不像ListView和
GridView控制元件有很多配置比如:android:dividerHeight ,android:divider,android:numColumns等。這是因
為RecyclerView雖然也是容器控制元件,大多數的效果顯示可以通過程式碼來控制顯示,但是RecyclerView更加自由,
更加包容,使用者更容易去定義它的內容顯示方
式。接下來通過程式碼看怎麼使用RecyclerView吧!
private void initViews() {
recylerView = findView(R.id.recyclerview);
//設定佈局顯示方式
recylerView.setLayoutManager(new LinearLayoutManager(this, LinearLayout.VERTICAL, true));
//設定新增刪除item時候的動畫
recylerView.setItemAnimator(new DefaultItemAnimator());
}
是不是感覺還是蠻簡單的嘛?就兩行程式碼,我們來看看setLayoutManager設定佈局顯示方式方法吧!
setLayoutManager()方法接受一個 LayoutManager 佈局管理引數。引數型別可以有以下幾種:
- LinearLayoutManager:線性佈局
- GridLayoutManager:網格佈局
- StaggeredGridLayoutManager:流式佈局
那麼怎麼new一個LayoutManager出來呢?舉個例子:
new LinearLayoutManager(this, LinearLayout.VERTICAL, true)
第一個引數 Context ,第二個引數:佈局方向LinearLayout.VERTICAL垂直和LinearLayout.HORIZONTAL水平,
第三個引數:表示是否從最後的Item資料開始顯示,ture表示是,false就是正常顯示—從開頭顯示。
setItemAnimator()方法的作用是設定當前RecyclerView容器有子Item改變時(新增item或者刪除item)導致
整個佈局的動畫效果。一般我們new 一個系統預設的動畫出來就好了。
RecyclerView介面卡
既然RecyclerView是一個容器控制元件,那麼裡面總的裝載內容吧,也就是的有一個自己的介面卡。來看看介面卡怎麼
實現的吧!
/**
* Description:RecyclerView 介面卡
* User: xjp
* Date: 2015/6/8
* Time: 10:15
*/
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
private Context context;
private List<ModelBean> list;
private Resources res;
public RecyclerAdapter(Context context, List<ModelBean> list) {
this.context = context;
this.list = list;
res = context.getResources();
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_card_view, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
final ModelBean bean = list.get(position);
holder.title.setText(bean.getTitle());
holder.imageView.setImageResource(bean.getResId());
}
@Override
public int getItemCount() {
return null == list ? 0 : list.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView title;
public MyViewHolder(View view) {
super(view);
imageView = (ImageView) view.findViewById(R.id.pic);
title = (TextView) view.findViewById(R.id.name);
}
}
}
看到嘛?我們是繼承RecyclerView.Adapter類,實現裡面的抽象方法即可。可以看到RecyclerView.Adapter適配
器裡面有一套完整的機制來控制之ItemView的查詢和顯示。通過內部類MyViewHolder繼承
RecyclerView.ViewHolder封裝容器中的ItemView,實現onCreateViewHolder抽象方法來載入ItemView的布
局,實現onBindViewHolder抽象方法來繫結容器中的ItemView,進而進行賦值。
ItemView點選事件
細心的你會發現,很遺憾的是RecyclerView沒有提供setItemOnClickListener點選監聽方法。那麼我們要監聽每個
ItemView的點選事件怎麼辦呢?沒關係!我們來看看程式碼中怎麼實現吧!
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
final ModelBean bean = list.get(position);
holder.title.setText(bean.getTitle());
holder.imageView.setImageResource(bean.getResId());
/**
* 呼叫介面回撥
*/
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != listener)
listener.onItemClick(position, bean);
}
});
}
/**
* 內部介面回撥方法
*/
public interface OnItemClickListener {
void onItemClick(int position, Object object);
}
/**
* 設定監聽方法
*
* @param listener
*/
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
在RecyclerView的介面卡類中定義了一個OnItemClickListener介面,然後在onBindViewHolder方法中設定每個holder.itemView的點選事件,外面呼叫setOnItemClickListener方法即可
adapter.setOnItemClickListener(new RecyclerAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position, Object object) {
Toast.makeText(MainActivity.this, ((ModelBean) object).getTitle(), Toast.LENGTH_SHORT).show();
}
});
RecyclerView新增,刪除,更新資料
和ListView,GridView容器不一樣的是,RecyclerView容器更新資料的方法有很多,不信你看:
- notifyDataSetChanged():更新所有資料
- notifyItemInserted(int position):在position位置插入資料的時候更新
- notifyItemRemoved(int position):移除postion位置的資料的時候更新
- notifyItemChanged(int position):當postion位置資料有改變時候更新
- notifyItemMoved(int fromPosition, int toPosition):移除從位置formPosition到toPosition位置資料更新
- notifyItemRangeChanged(int positionStart, int itemCount)
- notifyItemRangeInserted(int positionStart, int itemCount)
- notifyItemRangeRemoved(int positionStart, int itemCount)
如果你在程式碼裡設定了RecyclerView的ItemView改變時有動畫效果的話
recylerView.setItemAnimator(new DefaultItemAnimator());
在RecyclerView介面卡更新資料的時候就會有系統預設的動畫效果,童鞋們可以根據下面提供的原始碼看效果。
RecyclerView控制元件使用基本就
是以上內容,最後我會提供一個RecyclerView,CardView,Palette一起使用的原始碼例子,接著往下看吧騷年!
CardView控制元件
CardView稱之為卡片,也是Android5.0推出來的 Support v7包裡的widget,CardView是繼承自FrameLayout。
從XML中看它是怎麼使用的
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card="http://schemas.android.com/apk/res-auto"
android:layout_width="160dp"
android:layout_height="200dp"
android:layout_gravity="center"
card:cardBackgroundColor="@android:color/white"
card:cardCornerRadius="0dp"
card:cardElevation="2dp"
card:cardMaxElevation="@dimen/cardview_default_elevation">
<!--cardMaxElevation:最大卡片陰影的寬度-->
<!--cardElevation:卡片陰影的寬度-->
<!--cardBackgroundColor:卡片的背景顏色-->
<!--cardCornerRadius :卡片的圓角半徑-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/pic"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_centerInParent="true"
android:layout_weight="3"
android:scaleType="fitXY"
android:src="@drawable/img1" />
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:clickable="true"
android:gravity="center"
android:padding="5dp"
android:text="圖片描述"
android:textColor="@android:color/black"
android:textSize="16sp" />
</LinearLayout>
</android.support.v7.widget.CardView>
在使用CardView的時候需要引入屬性名稱空間,也就是加入以下程式碼
xmlns:card="http://schemas.android.com/apk/res-auto"
和自定義控制元件一樣,引入名稱空間是為了使用CardView的屬性值,名字“card”可以任意。來看看CardView有哪
些常用的屬性吧!
- cardElevation:卡片陰影的寬度
- cardMaxElevation:最大卡片陰影的寬度
- cardBackgroundColor:卡片的背景顏色
- cardCornerRadius :卡片的圓角半徑
CardView只需要在XML佈局檔案裡配置各種引數,程式碼中無需任何操作。其實CardView就是一個FrameLayout容器控制元件,只是添加了一些屬性而已,使用很簡單,只要掌握以上四種屬性配置即可。
注意:關於android5.0以上使用 v7包的CardView沒有陰影效果的問題。這裡直接給出答案:在CardView 新增如下屬性即可:
card:cardPreventCornerOverlap="true"
card:cardUseCompatPadding="true"
Palette
Palette類也是Android5.0引進來的一個獲取Bitmap顏色值的一個類。google為了相容前面的版本也把這個類放在
了Support v7 Library包裡,需要使用該類可以在專案中引進
compile 'com.android.support:palette-v7:22.2.0'
我們從程式碼中來看看它是怎麼使用的
//非同步獲得bitmap圖片顏色值
Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
Palette.Swatch vibrant = palette.getVibrantSwatch();//有活力
if (vibrant != null) {
holder.title.setBackgroundColor(
vibrant.getRgb());
holder.title.setTextColor(
vibrant.getTitleTextColor());
}
}
});
由於在Android裝置中,對影象的處理有可能是耗時操作,因此,Palette類通過非同步介面onGenerated回撥的方法
來獲得Bitmap的顏色值。Palette類獲得的顏色值有以下幾種型別:
- Palette.Swatch a = palette.getVibrantSwatch();//有活力
- Palette.Swatch b = palette.getDarkVibrantSwatch();//有活力 暗色
- Palette.Swatch c = palette.getLightVibrantSwatch();//有活力 亮色
- Palette.Swatch d = palette.getMutedSwatch();//柔和
- Palette.Swatch e = palette.getDarkMutedSwatch();//柔和 暗色
- Palette.Swatch f = palette.getLightMutedSwatch();//柔和 亮色
我們從以上顏色中可以獲取到如下顏色值:
- int color1 = a.getBodyTextColor();//內容顏色
- int color2 = a.getTitleTextColor();//標題顏色
- int color3 = a.getRgb();//rgb顏色
綜合例子
通過以上的瞭解,這裡提供一個綜合的例子來看看RecyclerView,CardView,Palette結合的使用。以後給出
MainActivity和Adapter的實現
package com.xjp.androidmddemo.activity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.xjp.androidmddemo.R;
import com.xjp.androidmddemo.activity.adapter.RecyclerAdapter;
import com.xjp.androidmddemo.activity.model.ModelBean;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener, View.OnClickListener {
protected Toolbar toolbar;
protected TextView title;
protected Spinner spinner;
protected Button add;
private RecyclerView recylerView;
private List<ModelBean> beanList;
private RecyclerAdapter adapter;
private String des[] = {"雲層裡的陽光", "好美的海灘", "好美的海灘", "夕陽西下的美景", "夕陽西下的美景"
, "夕陽西下的美景", "夕陽西下的美景", "夕陽西下的美景", "好美的海灘"};
private int resId[] = {R.drawable.img1, R.drawable.img2, R.drawable.img2, R.drawable.img3,
R.drawable.img4, R.drawable.img5, R.drawable.img3, R.drawable.img1};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initData();
initSpinner();
}
private void initSpinner() {
String categorys[] = this.getResources().getStringArray(R.array.categorys);
ArrayAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, categorys);
// 為adapter設定下拉選單樣式
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// spinner設定adapter
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(this);
}
private void initViews() {
toolbar = findView(R.id.toolbar);
if (null != toolbar) {
setSupportActionBar(toolbar);
title = findView(R.id.toolbar_title);
spinner = findView(R.id.toolbar_category);
add = findView(R.id.button_add);
if (null != title) {
title.setText(getTitle());
}
}
add.setOnClickListener(this);
recylerView = findView(R.id.recyclerview);
//設定佈局顯示方式
recylerView.setLayoutManager(new LinearLayoutManager(this, LinearLayout.VERTICAL, true));
//設定新增刪除item時候的動畫
recylerView.setItemAnimator(new DefaultItemAnimator());
}
private void initData() {
beanList = new ArrayList<>();
for (int i = 0; i < 8; i++) {
ModelBean bean = new ModelBean();
bean.setResId(resId[i]);
bean.setTitle(des[i]);
beanList.add(bean);
}
adapter = new RecyclerAdapter(this, beanList);
recylerView.setAdapter(adapter);
adapter.setOnItemClickListener(new RecyclerAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position, Object object) {
Toast.makeText(MainActivity.this, ((ModelBean) object).getTitle(), Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
switch (position) {
case 0:
recylerView.setLayoutManager(new LinearLayoutManager(this, LinearLayout.VERTICAL, true));
break;
case 1:
recylerView.setLayoutManager(new LinearLayoutManager(this, LinearLayout.HORIZONTAL, true));
break;
case 2:
recylerView.setLayoutManager(new GridLayoutManager(this, 2));
break;
case 3:
recylerView.setLayoutManager(new GridLayoutManager(this, 2, LinearLayout.HORIZONTAL, true));
break;
case 4:
recylerView.setLayoutManager(new StaggeredGridLayoutManager(2, LinearLayout.VERTICAL));
break;
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
@Override
public void onClick(View v) {
ModelBean bean = new ModelBean();
bean.setTitle("這是新新增的");
bean.setResId(R.drawable.img5);
beanList.add(0, bean);
// adapter.notifyDataSetChanged();//更新全部資料
// adapter.notifyItemInserted(0);//在
// adapter.notifyItemRemoved(0);
// adapter.notifyItemChanged(0);
// adapter.notifyItemMoved(0,1);
// adapter.notifyItemRangeChanged(0,2);
// adapter.notifyItemRangeInserted(0,2);
// adapter.notifyItemRangeRemoved(0,2);
}
protected <T extends View> T findView(int id) {
return (T) findViewById(id);
}
}
............................
package com.xjp.androidmddemo.activity.adapter;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.graphics.Palette;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.xjp.androidmddemo.R;
import com.xjp.androidmddemo.activity.model.ModelBean;
import java.util.List;
/**
* Description:RecyclerView 介面卡
* User: xjp
* Date: 2015/6/8
* Time: 10:15
*/
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
private Context context;
private List<ModelBean> list;
private Resources res;
private OnItemClickListener listener;
public RecyclerAdapter(Context context, List<ModelBean> list) {
this.context = context;
this.list = list;
res = context.getResources();
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_card_view, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
final ModelBean bean = list.get(position);
holder.title.setText(bean.getTitle());
holder.imageView.setImageResource(bean.getResId());
Bitmap bitmap = BitmapFactory.decodeResource(res, bean.getResId());
//非同步獲得bitmap圖片顏色值
Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
Palette.Swatch vibrant = palette.getVibrantSwatch();//有活力
Palette.Swatch c = palette.getDarkVibrantSwatch();//有活力 暗色
Palette.Swatch d = palette.getLightVibrantSwatch();//有活力 亮色
Palette.Swatch f = palette.getMutedSwatch();//柔和
Palette.Swatch a = palette.getDarkMutedSwatch();//柔和 暗色
Palette.Swatch b = palette.getLightMutedSwatch();//柔和 亮色
if (vibrant != null) {
int color1 = vibrant.getBodyTextColor();//內容顏色
int color2 = vibrant.getTitleTextColor();//標題顏色
int color3 = vibrant.getRgb();//rgb顏色
holder.title.setBackgroundColor(
vibrant.getRgb());
holder.title.setTextColor(
vibrant.getTitleTextColor());
}
}
});
/**
* 呼叫介面回撥
*/
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != listener)
listener.onItemClick(position, bean);
}
});
}
@Override
public int getItemCount() {
return null == list ? 0 : list.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView title;
public MyViewHolder(View view) {
super(view);
imageView = (ImageView) view.findViewById(R.id.pic);
title = (TextView) view.findViewById(R.id.name);
}
}
/**
* 內部介面回撥方法
*/
public interface OnItemClickListener {
void onItemClick(int position, Object object);
}
/**
* 設定監聽方法
*
* @param listener
*/
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
}
不理解的可以留言一起討論!