Android数据通信开发与应用(四):实战开发

目录

第一节:RecyclerView列表流行控件

一、RecyclerView是什么

二、RecyclerView的优点

三、编写一个简单的RecyclerView

1、导入RecyclerView依赖包

2、添加RecyclerView控件

3、创建item的布局文件item_layout.xml

4、创建适配器,这里使用了Gilde需要导包。

5、为RecyclerView设置LayoutManager

6、创建适配器实例,并设置给RecyclerView

7、为RecyclerView添加点击事件。

8、切换布局,三种布局循环切换。

9、插入、删除item,并设置动画:

第二节:NDK入门

一、NDK简介

二、优缺点及使用场景

三、NDK的配置

Mac/Linux配置

Windows配置

Android Studio的配置

第三节:实战:有声阅读器

第四节:扩展学习--GIF介绍


第一节:RecyclerView列表流行控件

一、RecyclerView是什么

RecyclerView是support-v7包中的新组件,与经典的ListView相比,同样拥有item回收复用的功能。

二、RecyclerView的优点

RecyclerView是ListView的升级版,有如下优点:

(一)RecyclerView封装了ViewHolder的回收复用

(二)提供了一种插拔式的体验,高度的解耦,异常的灵活,内置了三种LayoutManager:

  • LinearLayoutManager--横向或纵向滑动的列表
  • GridLayoutManager--类似于GridView的效果
  • StaggeredGridLayoutManager--可以实现瀑布流的效果

(三)可以控制Item增删的动画,并支持自定义动画

三、编写一个简单的RecyclerView

1、导入RecyclerView依赖包

在build.gradle中添加

implementation 'com.android.support:appcompat-v7:28.+'
implementation 'com.android.support:recyclerview-v7:28.+'//注意recyclerview的版本号必须与appcompat版本号一致

*补充内容,如果Androidx则只需要导包:

implementation 'com.google.android.material:material:1.0.0'

2、添加RecyclerView控件

    

    

3、创建item的布局文件item_layout.xml




    

    

4、创建适配器,这里使用了Gilde需要导包。

implementation "com.github.bumptech.glide:glide:4.9.0"
public class MyRecyclerViewAdapter extends RecyclerView.Adapter {
    private Context mContext;
    private List mLists;

    public MyRecyclerViewAdapter(Context mContext) {
        this.mContext = mContext;
        this.mLists = new ArrayList<>();
    }

    public void setDataSource(List dataSource) {
        mLists = dataSource;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.item_layout, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
        //绑定数据
        holder.mTitle.setText(mLists.get(position));
        Glide.with(mContext).load(getIcon(position)).into(holder.mIcon);
    }

    @Override
    public int getItemCount() {
        return mLists.size();
    }

    private int getIcon(int position) {
        switch (position % 5) {
            case 0:
                return R.drawable.a;
            case 1:
                return R.drawable.b;
            case 2:
                return R.drawable.c;
            case 3:
                return R.drawable.d;
            case 4:
                return R.drawable.e;
            default:
                return R.drawable.tree;
        }
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        public View mItemView;
        public ImageView mIcon;
        public TextView mTitle;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            mItemView = itemView;
            mIcon = itemView.findViewById(R.id.iv_icon);
            mTitle = itemView.findViewById(R.id.tv_title);
        }
    }
}

5、为RecyclerView设置LayoutManager

        LinearLayoutManager layoutManager=new LinearLayoutManager(this);
        layoutManager.setOrientation(RecyclerView.VERTICAL);
        mRecyclerView.setLayoutManager(layoutManager);

6、创建适配器实例,并设置给RecyclerView

        //设置RecyclerView的配器
        mAdapter = new MyRecyclerViewAdapter(this);
        mRecyclerView.setAdapter(mAdapter);

7、为RecyclerView添加点击事件。

由于RecyclerView本身并没有提供item点击事件,所以需要在Adapter中手动添加

1)第一种方式(高耦合):直接在Viewholder内部类中,或者onBindViewHolder方法中添加点击事件

在ViewHolder类中添加点击事件,需要使用getAdapterPotion()或者getLayoutPosition()获取item位置:

class ViewHolder extends RecyclerView.ViewHolder {
        public View mItemView;
        public ImageView mIcon;
        public TextView mTitle;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            mItemView = itemView;
            mIcon = itemView.findViewById(R.id.iv_icon);
            mTitle = itemView.findViewById(R.id.tv_title);
            mItemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext, "点击了"+getAdapterPosition(), Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

或者onBindViewHOlder方法中添加点击事件,可以直接通过position参数获得item位置:

    public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
        //绑定数据
        holder.mTitle.setText(mLists.get(position));
        Glide.with(mContext).load(getIcon(position)).into(holder.mIcon);

        holder.mItemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(mContext, "点击了"+position, Toast.LENGTH_SHORT).show();
            }
        });
    }

2)第二种方式(低耦合):在Adapter中自定义接口实现点击事件

    private OnItemClickListener mOnItemClickListener;

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        mOnItemClickListener = onItemClickListener;
    }

    //定义接口
    interface OnItemClickListener {
        void onItemClick(int position);
    }

    ...

    public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
        ...
        //Item点击事件
        holder.mItemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mOnItemClickListener != null) {
                    mOnItemClickListener.onItemClick(position);
                }
            }
        });
    }

实现接口:

        //item点击事件
        mAdapter.setOnItemClickListener(new MyRecyclerViewAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(int position) {
                Toast.makeText(MainActivity.this, "第"+position+"数据被点击", Toast.LENGTH_SHORT).show();
            }
        });

8、切换布局,三种布局循环切换。

//切换布局
        mBtnChangeLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //利用反射获取布局
                if (mRecyclerView.getLayoutManager().getClass() == LinearLayoutManager.class) {
                    GridLayoutManager gridLayoutManager
                            = new GridLayoutManager(MainActivity.this, 2);
                    mRecyclerView.setLayoutManager(gridLayoutManager);
                } else if (mRecyclerView.getLayoutManager().getClass() == GridLayoutManager.class) {
                    StaggeredGridLayoutManager staggeredGridLayoutManager
                            = new StaggeredGridLayoutManager(2,
                            StaggeredGridLayoutManager.VERTICAL);
                    mRecyclerView.setLayoutManager(staggeredGridLayoutManager);
                }else{
                    LinearLayoutManager layoutManager=new LinearLayoutManager(MainActivity.this);
                    layoutManager.setOrientation(RecyclerView.VERTICAL);
                    mRecyclerView.setLayoutManager(layoutManager);
                }
            }
        });

在onBindViewHolder方法中,为瀑布流布局设置随机高度:

        //设置瀑布流布局随机高度
        if (mRecyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
            ViewGroup.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getRandomHeight());
            holder.mIcon.setLayoutParams(params);
        } else {
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(80, 80);
            holder.mIcon.setLayoutParams(params);
        }

...

    private int getRandomHeight() {
        return (int) (Math.random() * 1000);
    }

9、插入、删除item,并设置动画:

    public void insertData(int position) {
        mInsertPosition = position;
        mLists.add(position, "插入的数据");
        //直接刷新数据,无动画
        //notifyDataSetChanged();

        //显示插入动画,后面数据的位置不改变,需要手动调用notifyItemRangeChanged更改
        notifyItemInserted(position);
        //刷新item
        notifyItemRangeChanged(position, mLists.size() - position);
    }

    public void removeData(int position) {
        mInsertPosition = -1;
        mLists.remove(position);

        //直接刷新数据,无动画
        //notifyDataSetChanged();

        //显示插入动画,后面数据的位置不改变,需要手动调用notifyItemRangeChanged更改
        notifyItemRemoved(position);
        //刷新item
        notifyItemRangeChanged(position, mLists.size() - position);
    }

为新插入的item设置单独的背景色

        //为插入的数据设置不同的背景色
        if (position == mInsertPosition) {
            holder.mItemView.setBackgroundColor(Color.RED);
        } else {
            holder.mItemView.setBackgroundColor(Color.parseColor("#bbeedd"));
        }

 

第二节:NDK入门

一、NDK简介

官方解释:NDK全称是Native Development Kit。NDK是一套允许开发人员将本地代码嵌入Android 应用程序包,可以将Android应用程序中的部分功能用C/C++语言来实现,并将这部分C/C++代码编译成可直接运行在Android平台上的本地代码。这些本地代码以so链接库的形式存在,并能自动将so和java应用一起打包成apk。

通俗解释:NDK允许开发人员用C/C++开发Android程序。

复习:Android四层结构:APP;Framework;基础库、运行时;Linux内核

运行机制:

Java代码->Class文件->ByteCode->Dex(运行与App层)

C代码->.o目标文件->So链接库(运行于Linux内核层)

Jni:Dex中java代码直接调用so链接库。

二、优缺点及使用场景

优点:

• Native代码执行效率高
• 反编译难度大,保密性好
• 可以直接接触底层系统
• Native代码嵌入式平台移植性好
• 方便使用各种开源库

缺点:

• 调用步骤繁琐
• 互调过程开销较大
• 需要处理资源分配与释放
• 要了解的知识更多

使用场景:

  1. 编写 Android 驱动
  2. 对执行效率有高要求
  3. 对底层系统或一些Native开源库有依赖
  4. 代码保密性高

三、NDK的配置

Mac/Linux配置

1、下载NDK

2、命令行:vi ~/.bash_profile

添加一行:export NDK_ROOT="~/...(ndk路径)"

在export PATH="..."中加入:$NDK_ROOT

3、source ~/.bash_profile,使配置生效。

Windows配置

电脑--右键--属性--高级系统设置--环境变量--在系统变量中新建(变量名:NDK_ROOT;变量值:ndk路径)--Path中添加NDK_ROOT

注:ndk使用命令行操作比较方便,cygwin是window下模拟unix的一个工具,推荐使用cygwin操作。

Android Studio的配置

SDK Manager--SDK Tools:NDK前打钩;LLDB前打钩;

Project Structure--SDK location--Android SDK location:选择sdk路径(...SDK\ndk-bundle)

第三节:实战:有声阅读器

Live Template的使用:是一个预定义的代码模板,其中的内容能够根据上下文信息自动推断。AS提供了一些定义好的缩写,如

fori、Toast、todo、psfi等,用户也可以自定义代码模块,如:

单例模式实现:

private volatile static $className$ sInstance;

public static $className$ getInstance() {
    if (sInstance == null) {
        synchronized ($className$.class) {
            if (sInstance == null) {
                sInstance = new $className$();
            }
        }
    }
    return sInstance;
}

private $className$() {
}

弱引用的静态handler

public static class MyHandler extends Handler{
        public final WeakReference<$className$> mWeakReference;
        public MyHandler($className$ activity) {
            mWeakReference=new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            $className$ activity = mWeakReference.get();
            if(msg.what==$code$){
                if(activity!=null){
                    
                }
            }
        }
    }

*注: 使用 @SerializedName注解,可以解决Gson解析时名称必须相同的问题

*使用GsonFormat插件快速生成实体类

https://blog.csdn.net/zhang_zxk/article/details/84195784

第四节:扩展学习--GIF介绍

你可能感兴趣的:(Android)