android 5.0 Material Design酷炫风格的开源项目集合
用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容器控件,只是添加了一些属性而已,使用很简单,只要掌握以上四种属性配置即可。
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;
}
}
来自:http://blog.csdn.net/feiduclear_up/article/details/46439005
Android 6.0 中新的新技术
最近 Google 正式发布了 Android M,并命名为“棉花糖”(Marshmallow),对于开发者来说,有哪些新的技术和变化呢?本文尝试总结一下 Android 6.0(API 23)给开发者带来新的技术和改变。
权限管理
权限管理是 Android M 最大的改变,权限管理更加精细,并且由以前的安装时静态授权,改为现在的运行时动态授权。大家对 Android 的权限吐槽已久,Android 应该能极大的改善这方面的问题。主要改变有:
- 系统设置中可以对 APP 各个权限单独控制
- 权限根据内容进行分组了
- 普通权限还是在安装时授权
- 其他权限在运行时系统弹窗授权,并且要解析使用这个权限的目的
对于开发者来说,需要小心处理权限相关的问题。在使用某个功能的时候,需要总是判断是否有改权限,并且通过合适的方式请求用户授权。关于 Android M 权限的处理,可以参考官方文档。
另外,这篇文章结合图文和代码讲述了 Android M 中权限模型,比较清晰,可以参考一下。
指纹解锁
Android 官方支持指纹认证,可以用在解锁,或者任何需要验证用户的地方,例如支付。提供了新的 API FingerprintManager
,让第三方 APP 来用来获得指纹认证的功能。具体使用方法可以参考这个实例。