月度归档:2016年04月

搞定Android DataBinding-02

RecyclerView.Adapter使用DataBinding

在Adapter中使用分三步:

  1. ViewHolder的创建
  2. 绑定ViewHolder
  3. Item数据的读取

ViewHolder的创建

inflate生成View,然后DataBindingUtil.bind(itemView)创建DataBinding
或者
RecyclerItemViewBinding.inflate()生成DataBinding,然后binding.getRoot()得到View
这里采用第一种方式

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recycler_item_view, parent, false);

        return new VehicleListAdapter.ViewHolder(view);
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        //binding 用来设置绑定值
        private ViewDataBinding binding;

        public ViewHolder(View itemView) {
            super(itemView);
            binding = DataBindingUtil.bind(itemView);
        }

        public ViewDataBinding getBinding() {
            return binding;
        }
    }

绑定ViewHolder

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        final VehicleInfo info = getItem(position);
        if(holder instanceof ViewHolder){
            ViewDataBinding binding = ((ViewHolder) holder).getBinding();
            binding.setVariable(BR.info, info);
            binding.setVariable(BR.itemCLick, itemListener);
            binding.setVariable(BR.selectedCLick, selectedListener);
            //等到绑定完成
            binding.executePendingBindings();
        }
    }

Item数据的读取

DataBindingUtil.findBinding(v) 方法,返回view的DataBinding,非常棒。

    OnViewClickListener itemListener = new OnViewClickListener() {

        @Override
        public void onClick(View v) {
            RecyclerItemViewBinding binding = DataBindingUtil.findBinding(v);
            VehicleInfo data = binding.getInfo();
            Toast.makeText(v.getContext(), data.getBrand(), Toast.LENGTH_SHORT).show();
        }
    };

NotifyDataChanged

Data Binding提供了三种Observable: Observable objects、Observable fields and Observable collections,分别表示观察对象、观察字段、观察集合,若相应的对象、字段、集合数据变化时候,那么将会自动更新UI。
这里直接使用ObservableBoolean,省去创建整个Observable object的繁琐。

    private ObservableBoolean isSelected;
    ...

    //set 之后,自动更新UI
    OnViewClickListener selectedListener = new OnViewClickListener() {

        @Override
        public void onClick(View v) {
            RecyclerItemViewBinding binding = DataBindingUtil.findBinding(v);
               VehicleInfo data = binding.getInfo();
               for(VehicleInfo e : getList()){
                   if(e.getIsSelected().get()) {
                       e.getIsSelected().set(false);
                       break;
                   }
               }
               data.getIsSelected().set(true);
        }
    };

源码

GitHub地址:AndroidDataBindingTutorial

搞定Android DataBinding-01

搞定Android DataBinding-00主要是变量的绑定,如果需要加载图片,怎么办呢?
实际上,绑定变量的时候调用的是setter方法(TextView的”android:text”就是调用setText(String)),那么就应该支持绑定到自己指定的方法。

BindingAdapter

绑定一个下载图片的方法:需要图片URL和ImageView来显示,让这个方法能直接调用,最简单的就先定义为static

    @BindingAdapter({"imageUrl"})
    public static void imageLoader(ImageView imageView, String url) {
        Picasso.with(imageView.getContext()).load(url).into(imageView);
    }

绑定了上面的方法,当一个ImageView设置了”imageUrl”属性,就会调用imageLoader()方法。

BindingAdapter 属性

这样定义好之后,就可以图片下载了。

    <ImageView 
        android:layout_margin="@dimen/activity_horizontal_margin" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="center" 
        android:scaleType="centerInside" 
        app:imageUrl='@{imgUrl ?? "http://0v7.jpg"}'/>

BindingAdapter 再加个参数

当加载文件失败的时候,使用失败的Drawable。
参数的顺序要一致:
{“imageUrl”, “error”}
(ImageView imageView, String url, Drawable error)

    @BindingAdapter({"imageUrl", "error"})
    public static void imageLoader(ImageView imageView, String url, Drawable error) {
        Picasso.with(imageView.getContext())
                .load(url)
                .error(error)
                .into(imageView);

    }

BindingAdapter 参数属性

ImageView定义的属性,可以用来当作imageLoader的函数参数。

    <ImageView 
        android:layout_margin="@dimen/activity_horizontal_margin" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="center" 
        android:scaleType="centerInside" 
        app:imageUrl='@{imgUrl ?? "http://0v7.jpg"}'
        app:error="@{@drawable/ic_image_load_error}"/>

几个方法说明

带ID的view可以直接访问

带ID的view可以直接访问,会生成public final成员。

    //布局文件中
    <Button android:id="@+id/btn_change_image" .../>
    //Binding.java 中
    public final Button btnChangeImage;

完全可以替代Butter Knife

    mBinding.btnChangeImage.setOnClickListener(this);

资源文件的引用

大多数资源文件都可以使用,不过theme、style还好像不行。

//可以使用的
@stringArray,@intArray,@typedArray,
@animator,@stateListAnimator,@color,@colorStateList
//属性使用单引号['],表达式中使用双引号["]:
bind:actInfo='@{map["change_color"]}'
//属性值使用双引号["],表达式中使用["] = &quot; 或者反引号[`]
bind:actInfo="@{map[&quot;change_image&quot;]}"
bind:actInfo="@{map[`activity_c`]}"

源码

GitHub地址:AndroidDataBindingTutorial

搞定Android DataBinding-00

最近才开始使用DataBinding,一发不可收拾。DataBinding可完成很多以前比较重复的事情,势必越来越流行。
怎么样算掌握了一个技术呢:如果你能综合所学知识,思考出合适的“方案”来实现功能,那么才算掌握了。

准备工作

Android Studio 2.0 和 Gradle 2.0.0

buildscript {
    ...
    dependencies {
        classpath 'com.android.tools.build:gradle:2.0.0'
    }
}

在需要使用dataBinding的项目和library的build.gradle中开启:

android {
    ....
    dataBinding {
        enabled = true
    }
}

DataBinding是什么

设置一个TextView的内容需要两步:

    mTvContent = (TextView) view.findViewById(R.id.tv_content);
    mTvContent.setText("Hello World");

在一个元素多的界面,会需要大量的重复这两步,重复的事情不是程序该做的么。
于是Butter Knife简化了第一步,然而DataBinding更进一步,来简化这个过程。

    ...
    android:text="@{textContent}"
    ...
    mBinding.setTextContent("Hello World");
    ...

通过setTextContent直接设置,元素多的界面,可以通过一个复杂对象来搞定。Data Binding可以让部分逻辑处理在layout中完成。
ps:dataBinding 支持Android 2.1 (API level 7+)

DataBinding做一个修改字体颜色的功能

Data Binding 布局文件:change_txt_color.xml

布局文件必须两部分:data 和 ViewGroup
设置字体的颜色需要一个变量:txtColor, int 类型

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable name="txtColor" type="int"/>
    </data>

    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal">

        <TextView android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_margin="@dimen/activity_horizontal_margin" android:textColor="@{txtColor != 0 ? txtColor : @color/colorPrimary}" android:text="DataBinding Color"/>

        <Button android:id="@+id/btn_change_color" android:layout_gravity="center_vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/activity_horizontal_margin" android:text="Change Color"/>

    </LinearLayout>

</layout>

Data Binding 数据内容

需要拿到Data Binding 布局文件中的两部分内容,然后设置数据。
Data Binding 布局文件会被Android Studio自动编译成文件名+Binding的class,这里会生成:ChangeTxtColorBinding.java

    private ChangeTxtColorBinding mBinding;

    @Override
    public void init(Bundle savedInstanceState) {
        //在Activity中:dataBinding + setContentView()
        mBinding = DataBindingUtil.setContentView(this, R.layout.change_txt_color);
        //下一节再讲
        mBinding.btnChangeColor.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        //直接修改颜色值
        mBinding.setTxtColor(getRandomColor());
    }

ChangeTxtColorBinding是什么样的

自动生成的java文件在:/build/intermediates/classes/debug/com.github.captain_miao.databinding.tutorial.databinding
ChangeTxtColorBinding[抽取的代码]中生成了TextView 和 mTxtColor变量。
其实原理很简单了,就是把那两步操作自动生成代码来完成了。

public class ChangeTxtColorBinding extends android.databinding.ViewDataBinding  {
    ...
    private final android.widget.TextView mboundView1;
    // variables
    private int mTxtColor;

    public boolean setVariable(int variableId, Object variable) {
        switch(variableId) {
            case BR.txtColor :
                setTxtColor((int) variable);
                return true;
        }
        return false;
    }

    public void setTxtColor(int txtColor) {
        this.mTxtColor = txtColor;
        synchronized(this) {
            mDirtyFlags |= 0x1L;
        }
        super.requestRebind();
    }
    public int getTxtColor() {
        return mTxtColor;
    }
    ...
}

几个方法说明

1. ChangeTxtColorBinding

    ChangeTxtColorBinding mBinding = DataBindingUtil.setContentView(this, R.layout.change_txt_color);

返回ChangeTxtColorBinding,并把R.layout.change_txt_color的view内容设置为Activity’s content view
等同于

    mBinding = ChangeTxtColorBinding.inflate(getLayoutInflater());
    setContentView(mBinding.getRoot());

2. setTxtColor

    mBinding.setTxtColor(getRandomColor());
    //or
    mBinding.setVariable(BR.txtColor, getRandomColor());

源码

GitHub地址:AndroidDataBindingTutorial

recyclerview工具集合

引入当前最好的功能,在基础上整合,一个recyclerview功能集合库,首先

感谢

Ultra Pull to Refresh for Android:https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh

Sticky Headers decorator for Android’s RecyclerView:https://github.com/timehop/sticky-headers-recyclerview

A custom RecyclerView which allows for an expandable view to be attached to each ViewHolder:https://github.com/bignerdranch/expandable-recycler-view

Easy sidebar for RecyclerView: https://github.com/CaMnter/EasyRecyclerViewSidebar

起因

由于老需要重复实现:下拉刷新、加载更多,偶尔还要加个HeaderView啥的,每次都要写一些一样的代码,何不做个二次封装,简化使用。
于是WrapperRecyclerView使用组合的方式集成:秋百万的下拉刷新、加载更多。
于是BaseWrapperRecyclerAdapter实现了AddHeader、AddFooter、StickyHeaders、Expandable、Indexable。

如何使用

Gradle

repositories {
    maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}

dependencies {
    compile compile 'com.github.captain-miao:recyclerviewutils:1.1.9'
}

使用WrapperRecyclerView(代替RecyclerView)

<?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="match_parent" android:background="@color/light_grey">

    <com.github.captain_miao.recyclerviewutils.WrapperRecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent">

    </com.github.captain_miao.recyclerviewutils.WrapperRecyclerView>
</RelativeLayout>

实现BaseWrapperRecyclerAdapter

//onCreateItemViewHolder代替onCreateViewHolder()
//onBindItemViewHolder代替onBindViewHolder()
public class SimpleAdapter extends BaseWrapperRecyclerAdapter<String, SimpleAdapter.ItemViewHolder> {

    @Override
    public ItemViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.list_item, parent, false);

        return new SimpleAdapter.ItemViewHolder(view);
    }


    @Override
    public void onBindItemViewHolder(SimpleAdapter.ItemViewHolder vh, int position) {
        vh.mTvContent.setText(getItem(position));
    }


    public class ItemViewHolder extends ClickableViewHolder{
        public TextView mTvContent;

        public ItemViewHolder(View view) {
            super(view);
            mTvContent = (TextView) view.findViewById(R.id.tv_content);
        }
    }

}

Activity使用

    private SimpleAdapter mAdapter;
    private WrapperRecyclerView mWrapperRecyclerView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        setContentView(R.layout.ac_refresh_recycler_view);
        ...

        mWrapperRecyclerView = (WrapperRecyclerView) findViewById(R.id.recycler_view);
        final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        mWrapperRecyclerView.setLayoutManager(linearLayoutManager);
        mAdapter = new SimpleAdapter(new ArrayList<String>());
        mAdapter.setLoadMoreFooterView(new DefaultLoadMoreFooterView(this));
        mWrapperRecyclerView.setAdapter(mAdapter);

        mWrapperRecyclerView.setRecyclerViewListener(new RefreshRecyclerViewListener() {
            @Override
            public void onRefresh() {

            }

            @Override
            public void onLoadMore(int pagination, int pageSize) {

            }
        });
        mWrapperRecyclerView.post(new Runnable() {
            @Override
            public void run() {
                mWrapperRecyclerView.autoRefresh();
            }
        });
    }

效果图:

refresh_and_load_more

StickyHeaders、Expandable

这两个都是实现对应的adapter就可以了(StickyRecyclerHeadersAdapter、ExpandableRecyclerAdapter)

Indexable

1. 右侧放一个EasySidebar
2. 点击和活动的时候,定位到对应RecyclerView位置

源码

GitHub地址:RecyclerViewUtils

brew install ffmpeg fail

install ffmpeg fail

$ brew install ffmpeg
==> Installing dependencies for ffmpeg: pkg-config, texi2html, yasm, x264, lame, libvo-aacenc, xvid, xz, libpng, freetype, libogg, libvorbis, lib
...
...
checking for gcc... clang
checking whether the C compiler works... no
configure: error: in `/private/tmp/pkg-config20160401-64918-z4en6e/pkg-config-0.28':
configure: error: C compiler cannot create executables
See `config.log' for more details
/usr/local/Library/ENV/4.3/gcc-4.2:332:in `exec': No such file or directory - /usr/local/Library/ENV/4.3/xcrun (Errno::ENOENT)
	from /usr/local/Library/ENV/4.3/gcc-4.2:332:in `<main>'
Warning: It appears you have MacPorts or Fink installed.

uninstall brew

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall)"

install brew

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

install ffmpeg success

brew install ffmpeg

Make GIF Snapshot for Android APP:

Make GIF Snapshot for Android APP