您的位置:首页 > 移动开发 > Android开发

Android练习项目之基于Studio构建Material Design风格谈话管理系统

2015-09-23 15:23 375 查看
话不多说,先看效果图



学习android开发有几个月了,一直都是做做单个功能的小练习,最近正好有个公司用混合模式开发AppCan做的一个谈话管理项目,我用android原生将其基本实现,除了用于练习客户端与服务器的通信方法,也是对Google大力推行的Material Design的一个实践与尝试。

开发平台IDE

本项目首先在Eclipse上进行开发,但是在引用GitHub上面各个大神的Material Design Library时,非常不方便,大部分都没有封装成jar包,最后果断放弃,转用Android Studio进行开发,虽然编译速度很慢,但是在添加支持库、依赖方面实在是太给力了,下面再详细说。

Material Design

Material Design是Google公司在2014年I/O大会上提出的一种全新的设计模式,即以扁平化为主,通过引入Z轴、阴影的变化、水波纹等来体现“Material(物质、材料)”的概念。

可以说MD是未来Android的发展方向,越来越多的成熟软件都采用了该设计模式,未来必将是一种设计趋势。

点击了解 Material Design中文版

本项目特点

本项目使用了作为Material Design主推的RecyclerViewCardView两种控件,用于代替ListView,更大程度上发挥了列表视图的功能与扩展能力,使用FloatActionButton实现浮动按钮。

使用Activity嵌套ViewPager嵌套Fragment来实现页面的作用滑动,点击Tab跳转。

使用AsyncHttpClient框架进行网络通信,并且通过cookie来保持登录状态,实现客户端登录持久化。

AsyncHttpClient框架

使用了GitHub上面大量的开源UI框架,实现了Material Design风格。

Material Design开源框架库集合

项目配置

添加项目依赖

[code]dependencies {
    compile 'com.android.support:recyclerview-v7:22.2.0'
    //v7包中的RecyclerView
    compile 'com.android.support:cardview-v7:22.2.0'
    //v7包中的CardView
    compile 'com.android.support:appcompat-v7:22.0.1'
    //v7包中的Appcompat
    compile 'com.blunderer:materialdesignlibrary:2.0.4'
    //GitHub开源MD框架库
    compile 'com.rengwuxian.materialedittext:library:2.1.4'
    //GitHub开源EditText库
    compile 'com.github.rey5137:material:1.2.1'
    //GitHub开源普通Button库
    compile 'com.loopj.android:android-async-http:1.4.8'
    //GitHub开源网络请求框架
    compile 'com.getbase:floatingactionbutton:1.10.0'
    //GitHub开源FAB浮动Button
}


只需在项目根目录下的build.gradle中添加上面几行代码,即可在编译时自动下载所需依赖包,这是我放弃Eclipse转用Studio最看重的一个方面,实在是太方便了。

1、启动闪屏页面实现

主流软件打开时都会有一个大概几秒钟的启动页面,此时后台可以进行网络初始化等操作。

我的xml布局就不说了,只放了两个TextView显示两行文字,

实现思路很简单,通过开启线程打开新的页面,给线程设置等待时间就可以了。

看源码:

SplashActivity.java

[code]public class SplashActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
        //此处开启线程,等待两秒后执行,进入登录页面。
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    Intent mIntent = new Intent(SplashActivity.this, LoginActivity.class);
                    startActivity(mIntent);
                    //此处使用淡入淡出的切换动画
                    overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
                    finish();

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}


其中淡入淡出的xml资源如下(淡出相反)

R.anim.fade_in.xml

[code]<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">

    <alpha
        android:duration="1000"//持续时间1s
        android:fromAlpha="0.0"//开始透明度0%
        android:toAlpha="1.0"//结束透明度100% />

</set>


2、登录页面实现

登录页面主体使用ActionBar,有一个服务器设置Button

EditText使用框架库,实现hint文字动态悬浮,获得焦点变色等效果。

CheckBox使用框架库,实现动画改变选中状态。

Button使用框架库,实现扁平化设计风格和点击ripple波纹效果。

activity_login.xml (只保留主要部分)

[code]<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <!-- 此处必须使用app命名空间,才可以改变自定义控件属性 -->

    <!-- 引用库中自定义EditText控件 -->
    <com.rengwuxian.materialedittext.MaterialEditText
        android:hint="Input your username"
        app:met_baseColor="@color/color_edit_text_unfocus"
        app:met_clearButton="true"
        app:met_floatingLabel="highlight"
        app:met_floatingLabelText="USERNAME"
        app:met_iconLeft="@drawable/ic_login_username"
        app:met_maxCharacters="10"
        app:met_primaryColor="@color/color_primary"
        app:met_singleLineEllipsis="true" />

    <!-- 密码输入EditText,同上省略 -->
    <com.rengwuxian.materialedittext.MaterialEditText
        android:inputType="textPassword"/>

    <!-- 记住密码CheckBox控件 -->
    <com.rey.material.widget.CheckBox
        android:id="@+id/cb_login_rempass"
        style="@style/Material.Drawable.CheckBox"
        android:text="Rember Password"
        android:textColor="@color/color_primary"
        app:cbd_strokeColor="@color/color_primary" />

    <!-- 登录Buttion -->
    <com.rey.material.widget.Button
    style="@style/Material.Drawable.Ripple.Touch.MatchView.Light"
        android:text="LOGIN"
        android:textColor="@color/carbon_white"
        app:rd_enable="true" />

</RelativeLayout>


登录页面Activity需要继承自com.blunderer.materialdesignlibrary.activities.Activity,然后重写需要重写的方法。

不要在onCreate中调用setContentView方法来设置布局资源

在重写方法getContentView中调用布局资源

需要单独封装AsyncHttpClient类,才能实现之后每次请求都保持登录状态,即设置cookie

使用SharePreferences来本地存储用户名和密码

创建用户信息类UserBean,通过get和set方法读写用户信息

创建服务器配置类ServerConfig来全局化配置服务器参数

通过重写onCreateOptionsMenu方法加载menu资源

通过重写onOptionsItemSelected设置menu监听

LoginActivity.java 实现源码

[code]public class LoginActivity extends com.blunderer.materialdesignlibrary.activities.Activity{

    private Button btn_login;
    //其他控件省略

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //此处不写setContentView(R.layout.activity_login)
        //布局加载在重写的getContentView方法中
        init();//初始化数据
        isSavedPassword();//自动填充密码
        setOnclickListener();//按钮监听
    }

    /* 初始化 */
    protected void init() {
        //绑定控件,其他省略
        et_username =(MaterialEditText)
        findViewById(R.id.et_login_username);
    }

    /* 自动填充密码 */
    protected void isSavedPassword() {

    //使用SharePreferences来存储用户名和密码
        SharedPreferences sharedPreferences = getSharedPreferences("UserLogin", MODE_PRIVATE);
        String username = sharedPreferences.getString("username", "");
        String password = sharedPreferences.getString("password", "");

        if (!(username.equals("")) && !(password.equals(""))) {
            cb_rempass.setChecked(true);
        }
        et_password.setText(password);
        et_username.setText(username);
    }

    /* 登陆按钮监听 */
    protected void setOnclickListener() {

        btn_login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //从服务器配置类中取出配置参数
                ServerConfig server = new ServerConfig();
                String flag = server.getFLAG();
                String url = server.getURL_USER_LOGIN();

                String username = et_username.getText().toString();
                String password = et_password.getText().toString();
                //判断输入是否为空
                if (!isEmptyInput(username, password)) {
                    doSavePassword(username, password);
                    //执行登录
                    doLogin(url, flag, username, password);
                }
            }
        });
    }

    /* 空输入判断 */
    protected boolean isEmptyInput(String username, String password) {
    //省略不写
    }

    /* 记住密码 */
    protected void doSavePassword(String username, String password) {

        SharedPreferences sharedPreferences = getSharedPreferences("UserLogin", MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        //如果CheckBox选中的话则存入帐号密码,否则存入空
        if (cb_rempass.isChecked()) {
            editor.putString("username", username);
            editor.putString("password", password);
            editor.commit();
        } else {
            editor.putString("username", "");
            editor.putString("password", "");
            editor.commit();
        }
    }

    /* 执行登录 */
    protected void doLogin(String url, String flag, String username, String password) {

        // 实例化client对象,我把AsyncHttpClient另外封装为一个类,增加setCookie功能
        AsyncHttpClient client = new FinalAsyncHttpClient().getAsyncHttpClient();

        //不知道为什么,在登录前就要执行保存cookie,否则会失败
        saveCookie(client);

        // 设置请求参数
        RequestParams params = new RequestParams();
        params.put("flag", flag);
        params.put("username", username);
        params.put("password", password);

        // 执行post请求
        client.post(url, params, new AsyncHttpResponseHandler() {

            //登录成功
            @Override
            public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
                try {
                    JSONObject jo = new JSONObject(new String(responseBody));
                    if (jo.getString("result").equals("登陆成功")) {
                        // 存储登录成功cookie
                        CookieUtils.setCookies(getCookie());
                        // 存储用户信息
                        saveUserInfoByString(new String(responseBody));
                        // 跳转至主页面
                        toMainActivity();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //登录失败
            @Override
            public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
                Toast.makeText(getApplicationContext(), error.toString(), Toast.LENGTH_SHORT).show();
                System.out.println("login登录失败:" + error.toString());
            }

        });
    }

    /* 用户信息存储 */
    protected void saveUserInfoByString(final String userJson) {
                try {
                    //实例化用户信息类UserBean,用于存储用户各项信息,此处省略
                    UserBean ub = new UserBean();
                    JSONObject user = new JSONObject(userJson);
                    ub.setUserId(user.getString("userId"));
                    } catch (JSONException e) {
                    e.printStackTrace();
                }
    }

    /* 跳转主页 */
    protected void toMainActivity() {
        Intent mIntent = new Intent(LoginActivity.this, MainActivity.class);
        this.startActivity(mIntent);
        this.finish();
    }

    /* 保存cookie */
    protected void saveCookie(AsyncHttpClient client) {
        PersistentCookieStore cookieStore = new PersistentCookieStore(this);
        client.setCookieStore(cookieStore);
    }

    /* 得到cookie */
    protected List<Cookie> getCookie() {
        PersistentCookieStore cookieStore = new PersistentCookieStore(this);
        List<Cookie> cookies = cookieStore.getCookies();
        return cookies;
    }

    //此处返回布局资源id
    @Override
    protected int getContentView() {
        return R.layout.activity_login;
    }

    //允许ActionBar阴影
    @Override
    protected boolean enableActionBarShadow() {
        return true;
    }

    //ActionBarHandler
    @Override
    protected ActionBarHandler getActionBarHandler() {
        return new ActionBarDefaultHandler(this);
    }

    //设置Option菜单
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //此处调用menu资源
        getMenuInflater().inflate(R.menu.menu_login, menu);
        return super.onCreateOptionsMenu(menu);
    }

    //设置menu菜单点击事件
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            //服务器设置按钮
            case R.id.btn_login_server:
                Intent intent = new Intent(LoginActivity.this, ServerSettingActivity.class);
                startActivity(intent);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}


用户信息类示例 UserBean.java

[code]public final class UserBean {

    private static String userId;//用户信息变量
    //get方法
    public String getUserId() {
        return userId;
    }
    //set方法
    public void setUserId(String userId) {
        UserBean.userId = userId;
    }
}


AsyncHttpClient封装类

[code]public class FinalAsyncHttpClient {

    private AsyncHttpClient client;

    /* 构造方法 */
    public FinalAsyncHttpClient() {
        client = new AsyncHttpClient();//实例化对象
        client.setTimeout(5);//设置超时
        // 获取cookie列表
        if (CookieUtils.getCookies() != null) {
            BasicCookieStore bcs = new BasicCookieStore();
            bcs.addCookies(CookieUtils.getCookies().toArray(
                    new Cookie[CookieUtils.getCookies().size()]));
            client.setCookieStore(bcs);
        }
    }

    /* 得到client对象方法 */
    public AsyncHttpClient getAsyncHttpClient() {
        return this.client;
    }
}


cookie处理类

[code]public class CookieUtils {

    private static List<Cookie> cookies;

    /* 返回cookies列表 */
    public static List<Cookie> getCookies() {
        return cookies != null ? cookies : new ArrayList<Cookie>();
    }

    /* 设置cookies列表 */
    public static void setCookies(List<Cookie> cookies) {
        CookieUtils.cookies = cookies;
    }

}


3、主页面实现

主页面继承自com.blunderer.materialdesignlibrary.activities.ViewPagerWithTabsActivity

重写必须重写的方法

在getViewPagerHandler方法中加载Fragment对象

expandTabs方法中,true:扩展Tab占满屏幕宽,false相反

defaultViewPagerPageSelectedPosition方法设置默认选择tab

onCreateOptionsMenu方法加载menu资源

onOptionsItemSelected方法设置menu点击事件

在点击注销按钮时创建一个dialog,此处需要注意

AlertDialog.Builder builder = new AlertDialog.Builder(this);

此时参数Context可以传入this,因为是在Activity类中;

若在Fragment中,则不能使用this或是getApplicationContext( ),要使用getActivity( ),否则报错

MainActivity.java

[code]public class MainActivity extends ViewPagerWithTabsActivity {

    @Override
    protected boolean enableActionBarShadow() {
        return true;//允许阴影
    }

    @Override
    public ViewPagerHandler getViewPagerHandler() {
        //通过ViewPagerHandler添加Fragment页面
        ViewPagerHandler handler = new ViewPagerHandler(this);
        handler.addPage("页面标题一", TalkCheckFragment.newInstance());
        handler.addPage("页面标题二", UserInfoFragment.newInstance());
        handler.addPage("页面标题三", TalkLookFragment.newInstance());
        return handler;
    }

    @Override
    public boolean expandTabs() {
        return true;//是否让Tab平均占满屏幕宽度
    }

    @Override
    public int defaultViewPagerPageSelectedPosition() {
        return 1;//进入页面时默认打开的Tab,从0开始
    }

    @Override
    protected ActionBarHandler getActionBarHandler() {
        return new ActionBarDefaultHandler(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        //加载menu资源
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return super.onCreateOptionsMenu(menu);
    }

    //menu中注销按钮点击事件
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            case R.id.btn_main_menu:
                //弹出注销提示框
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("提示");
                builder.setMessage("确认要注销当前用户吗?");
                builder.setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        Intent intent = new Intent(MainActivity.this, LoginActivity.class);
                        startActivity(intent);
                        finish();
                    }
                });
                builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
                builder.create().show();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}


4、谈话列表页面Fragment实现

谈话列表页面使用RecyclerView+CardView组合方式,实现灵活多变的卡片式列表布局,并且根据数据内容而显示不同的效果,以及卡片点击事件监听。

首先看看布局资源

fragment_talk_list.xml

[code]<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fab="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <FrameLayout 
        android:id="@+id/frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_main_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity" />

    </FrameLayout>

    <!-- 悬浮按钮控件 -->
    <com.getbase.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab_list_newTalk"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/frame"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_marginBottom="16dp"
        fab:fab_colorNormal="@color/color_primary"
        fab:fab_colorPressed="@color/color_primary_dark"
        fab:fab_icon="@drawable/ic_fab_star" />
</RelativeLayout>


下面看Fragment实现源码

继承自android.support.v4.app.Fragment

onCreateView方法用于加载布局资源

newInstance方法用于获取Fragment实例

调用jsonArrayToMapList方法将服务端返回JsonArray转存为List< Map >对象

bindToListView方法把数据绑定到CardView中显示

[code]public class TalkCheckFragment extends Fragment {

    private RecyclerView mRecyclerView;
    private TalkListAdapter mTalkListAdapter;
    private FloatingActionButton fab_createTalk;

    /* 得到Fragment对象实例 */
    public static TalkCheckFragment newInstance() {
        TalkCheckFragment fragment = new TalkCheckFragment();
        return fragment;
    }

    /* 获取谈话列表 */
    private void getTalkList(String url, String userId) {

        AsyncHttpClient client = new FinalAsyncHttpClient().getAsyncHttpClient();
        RequestParams params = new RequestParams();
        params.put("userid", userId);
        params.put("flag", "2");

        client.get(url, params, new AsyncHttpResponseHandler() {

            @Override
            public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
                //获取服务器资源成功后转换JsonArray数据
                List<HashMap<String, String>> data = jsonArrayToMapList(new String(responseBody));
                bindToListView(data);
            }

    /* 将JsonArray转存为List<Map> */
    private List<HashMap<String, String>> jsonArrayToMapList(String result) {

        List<HashMap<String, String>> data = new ArrayList<>();
        try {
            JSONArray ja = new JSONArray(result);
            for (int i = 0; i < ja.length(); i++) {
                HashMap<String, String> talk = new HashMap<>();
                JSONObject jo = ja.getJSONObject(i);
                Iterator<String> it = jo.keys();
                while (it.hasNext()) {
                    String key = String.valueOf(it.next());
                    String value = (String) jo.get(key);
                    talk.put(key, value);
                }
                data.add(talk);
            }
        } 
        return data;
    }

    /* 绑定数据集到列表视图 */
    private void bindToListView(List<HashMap<String, String>> data) {
        //给RecyclerView设置线性布局管理器
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        //设置默认item属性动画
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mRecyclerView.setHasFixedSize(true);
        //实例化自定义适配器
        mTalkListAdapter = new TalkListAdapter(getActivity(), data);
        //设置卡片视图点击事件监听器
        //OnCardViewClickListener为抽象接口,需要实现onItemClick方法
        mTalkListAdapter.setOnCardViewClickListener(new TalkListAdapter.OnCardViewClickListener() {
            @Override
            public void onItemClick(View view, Map<String, String> data) {
                String talkId = data.get("id");
                System.out.println("talkid="+talkId);    
            }
        });
        //绑定适配器
        mRecyclerView.setAdapter(mTalkListAdapter);
    }

    /* FAB悬浮按钮监听 */
    private void setFabListener() {
        fab_createTalk.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //弹出新建谈话提示框
                AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                builder.setTitle("提示");
                builder.setMessage("确定要新建谈话吗?");
                builder.setPositiveButton("我要新建谈话", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        Intent intent = new Intent(getActivity(), NewTalkActivity.class);
                        startActivity(intent);
                    }
                });
                builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
                builder.create().show();
            }
        });
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        //得到谈话列表Fragment视图对象
        View view = inflater.inflate(R.layout.fragment_talk_list, container, false);

        //根据View对象绑定RecyclerView控件
        mRecyclerView = (RecyclerView) view.findViewById(R.id.rv_main_list);

        //设置FAB悬浮按钮监听
        fab_createTalk = (FloatingActionButton) view.findViewById(R.id.fab_list_newTalk);
        setFabListener();

        //请求服务器获取谈话列表
        String url = new ServerConfig().getURL_TALK_LIST();
        String userId = new UserBean().getUserId();
        getTalkList(url, userId);

        //返回视图
        return view;
    }

}


5、RecyclerView自定义Adapter实现

RecyclerView并没有ListView的onItemClickListener方法, 需要我们自己写

需要自己写ViewHolder类并且继承自RecyclerView.ViewHolder

需要自己写卡片点击事件监听器接口OnCardViewClickListener

onCreateViewHolder方法中加载CardView布局资源

该Adapter需要继承RecyclerView.Adapter< TalkListAdapter.ViewHolder >,泛型需要指定为自定义的ViewHolder类

该Adapter需要实现View.OnClickListener接口,在onClick方法中调用自定义的卡片点击监听器接口的onItemClick方法。

[code]public class TalkListAdapter extends RecyclerView.Adapter<TalkListAdapter.ViewHolder> 
implements View.OnClickListener {

    private List<HashMap<String, String>> mDataSet;//数据集
    private Context mContext;//上下文对象
    private OnCardViewClickListener mCardViewListener = null;//自定义卡片点击监听器

    /* 构造方法,在实例化适配器时传入上下文和数据集 */
    public TalkListAdapter(Context context, List<HashMap<String, String>> dataSet) {
        this.mContext = context;
        this.mDataSet = dataSet;
    }

    /* 创建卡片视图,即单个数据列表项Item */
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_card_view, parent, false);
        //实例化viewHolder对象,传入卡片视图View对象
        ViewHolder viewHolder = new ViewHolder(v);
        //设置卡片点击监听
        v.setOnClickListener(this);
        return viewHolder;
    }

    /* 绑定ViewHolder,将数据显示到对应控件上 */
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {

        //根据position取出数据集中数据,并绑定至ViewHolder中对应控件
        HashMap<String, String> item = mDataSet.get(position);

        //取出数据并显示在控件中
        holder.tv_title.setText(item.get("talktitle"));

        //设置Tag标签用于点击事件得到数据
        holder.itemView.setTag(item);
    }

    /* 得到数据集长度 */
    @Override
    public int getItemCount() {
        return mDataSet == null ? 0 : mDataSet.size();
    }

    /* 实现OnClick接口方法 */
    @Override
    public void onClick(View v) {

        //当卡片视图监听器不为null时,则执行监听器接口方法
        if (mCardViewListener != null) {

            //通过v.getTag得到数据,强转为Map类型
            mCardViewListener.onItemClick(v, (Map<String, String>) v.getTag());
        }
    }

    /* 绑定卡片视图点击事件监听器 */
    public void setOnCardViewClickListener(OnCardViewClickListener listener) {
        this.mCardViewListener = listener;
    }

    /* 必须实现自定义ViewHolder,继承自RecyclerView.ViewHolder */
    public static class ViewHolder extends RecyclerView.ViewHolder {

        //成员变量
        public TextView tv_title, tv_state, tv_person, tv_org, tv_date;
        public View iv_header;

        //构造方法,传入View对象,绑定控件id
        public ViewHolder(View v) {
            super(v);
            tv_title = (TextView) v.findViewById(R.id.tv_list_title);
            tv_state = (TextView) v.findViewById(R.id.tv_list_state);
            tv_date = (TextView) v.findViewById(R.id.tv_list_date);
            tv_org = (TextView) v.findViewById(R.id.tv_list_org);
            tv_person = (TextView) v.findViewById(R.id.tv_list_person);
            iv_header = v.findViewById(R.id.img_card_header);
        }
    }

    /* 卡片点击事件监听接口 */
    public interface OnCardViewClickListener {
        void onItemClick(View view, Map<String, String> data);
    }
}


至此,该项目的主要功能基本就实现了,一些比较简单的就没写出,主要是给自己的项目练习做个总结,方便以后有需要的时候回顾,也希望能让更多初学者能从中多少获得一些收获,共同进步。

再次表达一下对Material Design的喜爱之情,真的是太棒了,看看那些大神们封装的各种控件库,真的是美的不行不行的,完全秒杀iPhone的界面设计了我觉得。

有问题可私信我,有共同学习的小伙伴联系我,需要源码也可以找我。QQ12547345
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: