美文网首页高级UI优秀案例自定义控件
Android中RecyclerView配合BaseRecycl

Android中RecyclerView配合BaseRecycl

作者: 马木木_1991 | 来源:发表于2019-10-12 16:18 被阅读0次

    今天来使用BaseRecyclerViewAdapterHelper来完成瀑布流效果。

    说明:

    一,使用的Androidstudio版本为3.5(最新版)。

    二,看过很多网络的瀑布流实现效果,或多或少都会出现各种问题,比如上下滑动的时候RecyclerView顶部留白,中间存在空隙。加载卡顿或者直接崩溃。本文彻底解决这些疑难杂症。

    三,这是BaseRecyclerViewAdapterHelper的系列的第九篇文章,如有简单的不懂使用请看前面的文章。

    原作的github地址为:https://github.com/CymChad/BaseRecyclerViewAdapterHelper

    展示效果:

    效果图
    Screenrecorder.gif

    现在正式开始

    1,先看gradle文件,加一些需要使用的相关依赖,***为核心依赖库

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 29
        defaultConfig {
            applicationId "com.mumu.jsrecyclerview8"
            minSdkVersion 19
            targetSdkVersion 29
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
    
        //1,支持1.8
        compileOptions {
            targetCompatibility 1.8
            sourceCompatibility 1.8
        }
    }
    
    //,2,增加itpack支持
    allprojects {
        repositories {
            jcenter()
            maven { url 'https://jitpack.io' }
            maven { url "https://maven.google.com" }
    
        }
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'androidx.appcompat:appcompat:1.1.0'
        implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'androidx.test:runner:1.2.0'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    
        //3,增加相关依赖
        //butterKnife
        implementation 'com.jakewharton:butterknife:10.1.0'
        annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
        //androidx ***
        implementation 'androidx.recyclerview:recyclerview:1.0.0'
        //RecyclerView的适配器 ***
        implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.50'
        //通用广告栏ConvenientBanner 
        implementation 'com.bigkoo:convenientbanner:2.1.5'
        //增加下拉刷新SmartRefreshLayout的依赖 ***
        implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0-andx-14'
        implementation 'com.scwang.smartrefresh:SmartRefreshHeader:1.1.0-andx-14'
        implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    
        //FloatingActionButton ***
        api 'com.google.android.material:material:1.1.0-beta01'
        //标题栏
        api 'com.github.goweii:ActionBarEx:3.2.0'
        //增加一个图片加载库 ***
        api 'com.github.bumptech.glide:glide:4.9.0'
    
        //retrofit2网络框架
        api 'io.reactivex.rxjava2:rxjava:2.2.13'
        api 'io.reactivex.rxjava2:rxandroid:2.1.0'
        api 'com.squareup.retrofit2:retrofit:2.6.0'
        api 'com.squareup.retrofit2:converter-gson:2.5.0'
        api 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
        api 'com.squareup.okhttp3:logging-interceptor:3.10.0'
    }
    
    

    2,再看核心的mainactivity,核心在refreshView方法。注释写的很详细。

    package com.mumu.jsrecyclerview8;
    
    import android.animation.ObjectAnimator;
    import android.animation.PropertyValuesHolder;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.view.ViewConfiguration;
    import android.widget.ImageView;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.recyclerview.widget.RecyclerView;
    import androidx.recyclerview.widget.StaggeredGridLayoutManager;
    
    import com.bigkoo.convenientbanner.ConvenientBanner;
    import com.bigkoo.convenientbanner.holder.CBViewHolderCreator;
    import com.bigkoo.convenientbanner.holder.Holder;
    import com.bigkoo.convenientbanner.listener.OnItemClickListener;
    import com.bumptech.glide.Glide;
    import com.chad.library.adapter.base.BaseQuickAdapter;
    import com.google.android.material.floatingactionbutton.FloatingActionButton;
    import com.mumu.jsrecyclerview8.api.ApiUrl;
    import com.scwang.smartrefresh.layout.SmartRefreshLayout;
    import com.scwang.smartrefresh.layout.api.RefreshLayout;
    import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import butterknife.BindView;
    import butterknife.ButterKnife;
    import butterknife.OnClick;
    import per.goweii.actionbarex.common.ActionBarCommon;
    import retrofit2.Call;
    import retrofit2.Callback;
    import retrofit2.Response;
    import retrofit2.Retrofit;
    import retrofit2.converter.gson.GsonConverterFactory;
    
    public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.abc_main_return)
        ActionBarCommon abcMainReturn;
        @BindView(R.id.srl_main)
        SmartRefreshLayout srlMain;
        @BindView(R.id.fab_main)
        FloatingActionButton fabMain;
    
        private RecyclerView rvMain;
        private int distance;
        private boolean visible = true;
        private MainAdapter mMainAdapter;
        private View top;
        private ArrayList<String> arrayList = new ArrayList<>();
        private ViewHolder viewHolder;
        private boolean mCanLoop = true;
        private List<MainEntity.ResultsBean> mList = new ArrayList<>();
        private int mPage = 1;//页数
        private boolean isSrl = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
            rvMain = findViewById(R.id.rv_main);
    
            initView();
        }
    
        private void initView() {
            refreshView();
            initBanner();
            smartRefreshView();
            getPicCmd();
        }
    
        /**
         * 刷新消息列表
         */
        private void refreshView() {
            // 创建StaggeredGridLayoutManager实例
            MMStaggeredGridLayoutManager layoutManager =
                    new MMStaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
            layoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
            rvMain.setLayoutManager(layoutManager);
            //RecyclerView的滚动监听,是否展示FloatingActionButton
            rvMain.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                    //向下滚动
                    if (distance < -ViewConfiguration.getTouchSlop() && !visible) {
                        //显示fab
                        showFABAnimation(fabMain);
                        distance = 0;
                        visible = true;
                    } else if (distance > ViewConfiguration.getTouchSlop() && visible) {
                        //隐藏
                        hideFABAnimation(fabMain);
                        distance = 0;
                        visible = false;
                    }
                    //向下滑并且可见  或者  向上滑并且不可见
                    if ((dy > 0 && visible) || (dy < 0 && !visible)) {
                        distance += dy;
                    }
                    //滑动到顶部的时候隐藏按钮
                    if (!recyclerView.canScrollVertically(-1)) {
                        //隐藏
                        hideFABAnimation(fabMain);
                    }
                }
    
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    int[] first = new int[2];
                    layoutManager.findFirstCompletelyVisibleItemPositions(first);
                    if (newState == RecyclerView.SCROLL_STATE_IDLE && (first[0] == 1 || first[1] == 1)) {
                        layoutManager.invalidateSpanAssignments();
                    }
                }
            });
            mMainAdapter = new MainAdapter();
            rvMain.setAdapter(mMainAdapter);
            top = getLayoutInflater().inflate(R.layout.item_main_header, rvMain, false);
            mMainAdapter.addHeaderView(top);
            //因为要加载recyclerview中的header的资源,所以吧绑定放在这
            ButterKnife.bind(this);
            mMainAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
                @Override
                public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                    Toast.makeText(MainActivity.this, "你点击了第" + position + "张美女图片", Toast.LENGTH_SHORT).show();
                }
            });
        }
    
    
        private void initBanner() {
            arrayList.clear();
            arrayList.add("http://img2.imgtn.bdimg.com/it/u=1447362014,2103397884&fm=200&gp=0.jpg");
            arrayList.add("http://img1.imgtn.bdimg.com/it/u=111342610,3492888501&fm=26&gp=0.jpg");
            arrayList.add("http://imgsrc.baidu.com/imgad/pic/item/77094b36acaf2eddc8c37dc7861001e9390193e9.jpg");
            viewHolder = new ViewHolder(top);
            if (arrayList.size() <= 1) {
                mCanLoop = false;
            } else {
                mCanLoop = true;
            }
            viewHolder.cbMain.setPages(new CBViewHolderCreator() {
                @Override
                public Holder createHolder(View itemView) {
                    return new NetImageHolderView(itemView);
                }
    
                @Override
                public int getLayoutId() {
                    return R.layout.item_main_banner;
                }
            }, arrayList)
                    .setPageIndicator(new int[]{R.mipmap.ic_page_indicator, R.mipmap.ic_page_indicator_focused})
                    .setPageIndicatorAlign(ConvenientBanner.PageIndicatorAlign.CENTER_HORIZONTAL)
                    .setPointViewVisible(mCanLoop)
                    .setCanLoop(mCanLoop)
                    .setOnItemClickListener(new OnItemClickListener() {
                        @Override
                        public void onItemClick(int position) {
                            Toast.makeText(MainActivity.this, "你点击了第" + position + "张banner图片", Toast.LENGTH_SHORT).show();
                        }
                    });
            if (arrayList.size() > 0) {
                viewHolder.cbMain.startTurning(3000);
            }
    
            initReListener(viewHolder.tvMain1);
            initReListener(viewHolder.tvMain2);
            initReListener(viewHolder.tvMain3);
            initReListener(viewHolder.tvMain4);
            initReListener(viewHolder.tvMain5);
            initReListener(viewHolder.tvMain6);
            initReListener(viewHolder.tvMain7);
            initReListener(viewHolder.tvMain8);
        }
    
        private void initReListener(View view) {
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Toast.makeText(MainActivity.this, "暂未开放!",Toast.LENGTH_SHORT).show();
                }
            });
        }
    
        /**
         * 轮播图对应的holder
         */
        public class NetImageHolderView extends Holder<String> {
            private ImageView mImageView;
    
            //构造器
            public NetImageHolderView(View itemView) {
                super(itemView);
            }
    
            @Override
            protected void initView(View itemView) {
                //找到对应展示图片的imageview
                mImageView = itemView.findViewById(R.id.iv_banner);
                //设置图片加载模式为铺满,具体请搜索 ImageView.ScaleType.FIT_XY
                mImageView.setScaleType(ImageView.ScaleType.FIT_XY);
            }
    
            @Override
            public void updateUI(String data) {
                //使用glide加载更新图片
                Glide.with(MainActivity.this).load(data).into(mImageView);
            }
        }
    
        /**
         * by moos on 2017.8.21
         * func:显示fab动画
         */
        public void showFABAnimation(View view) {
            view.setVisibility(View.VISIBLE);
            PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f);
            PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f);
            PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f);
            ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ).setDuration(400).start();
    
        }
    
        /**
         * by moos on 2017.8.21
         * func:隐藏fab的动画
         */
    
        public void hideFABAnimation(View view) {
            PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 0f);
            PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 0f);
            PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 0f);
            ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ).setDuration(400).start();
            view.setVisibility(View.GONE);
        }
    
        @OnClick(R.id.fab_main)
        public void onViewClicked() {
            //缓慢滑动到顶部
            rvMain.smoothScrollToPosition(0);
        }
    
        static
        class ViewHolder {
            @BindView(R.id.cb_main)
            ConvenientBanner cbMain;
            @BindView(R.id.tv_main1)
            TextView tvMain1;
            @BindView(R.id.tv_main2)
            TextView tvMain2;
            @BindView(R.id.tv_main3)
            TextView tvMain3;
            @BindView(R.id.tv_main4)
            TextView tvMain4;
            @BindView(R.id.tv_main5)
            TextView tvMain5;
            @BindView(R.id.tv_main6)
            TextView tvMain6;
            @BindView(R.id.tv_main7)
            TextView tvMain7;
            @BindView(R.id.tv_main8)
            TextView tvMain8;
    
            ViewHolder(View view) {
                ButterKnife.bind(this, view);
            }
        }
    
        /**
         * MainActivity中增加下拉刷新和上拉加载的监听方法
         */
        private void smartRefreshView() {
            srlMain.setOnRefreshLoadMoreListener(new OnRefreshLoadMoreListener() {
                @Override
                public void onRefresh(@NonNull RefreshLayout refreshLayout) {
                    //下拉刷新,一般添加调用接口获取数据的方法
                    mPage = 1;
                    isSrl = true;
                    getPicCmd();
                }
    
                @Override
                public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
                    //上拉加载,一般添加调用接口获取更多数据的方法
                    mPage++;
                    //接口后面的图片为空,这样做目的是让图片都能加载
                    if(mPage>4){
                        mPage=2;
                    }
                    isSrl = true;
                    getPicCmd();
                }
            });
        }
    
        /**
         * 获取图片的请求
         */
        private void getPicCmd(){
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://gank.io/api/")
                    //设置数据解析器
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
            ApiUrl apiUrl=retrofit.create(ApiUrl.class);
            Call<BaseResponse<MainEntity.ResultsBean>> call = apiUrl.getPic(10,mPage);
            call.enqueue(new Callback<BaseResponse<MainEntity.ResultsBean>>() {
                             @Override
                             public void onResponse(Call<BaseResponse<MainEntity.ResultsBean>> call, Response<BaseResponse<MainEntity.ResultsBean>> response) {
                                 if (mPage == 1) {
                                     mList.clear();
                                 }
                                 if (response.body() != null&& response.body().getResults().size() > 0) {
                                     mList.addAll(response.body().getResults());
                                     if (isSrl && mPage != 1) {
                                         int start = mList.size();
                                         mMainAdapter.notifyItemRangeInserted(start, 10);
                                     } else {
                                         mMainAdapter.setNewData(mList);
                                     }
                                     isSrl = false;
    
                                     srlMain.finishRefresh();
                                     if (response.body() != null && response.body().getResults().size() >= 10) {
                                         srlMain.finishLoadMore();
                                     } else {
                                         srlMain.finishLoadMoreWithNoMoreData();
                                     }
                                 }
                             }
    
                             @Override
                             public void onFailure(Call<BaseResponse<MainEntity.ResultsBean>> call, Throwable t) {
                                 Log.e("mmm","errow "+t.getMessage());
                             }
                         }
            );
        }
    }
    
    

    3,再看recyclerview对应的适配器,核心在glide加载图片的方法。加载完成图片后,利用map集合存储对应图片的高度。(宽度自适应屏幕),这样在上拉后在下拉也不会出现错乱和定顶部留白。

    package com.mumu.jsrecyclerview8;
    
    import android.graphics.Bitmap;
    import android.text.TextUtils;
    import android.util.Log;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    
    import androidx.annotation.Nullable;
    
    import com.bumptech.glide.Glide;
    import com.bumptech.glide.load.DataSource;
    import com.bumptech.glide.load.engine.DiskCacheStrategy;
    import com.bumptech.glide.load.engine.GlideException;
    import com.bumptech.glide.request.RequestListener;
    import com.bumptech.glide.request.RequestOptions;
    import com.bumptech.glide.request.target.Target;
    import com.chad.library.adapter.base.BaseQuickAdapter;
    import com.chad.library.adapter.base.BaseViewHolder;
    
    import java.util.HashMap;
    import java.util.List;
    
    import static com.mumu.jsrecyclerview8.App.getContext;
    
    /**
     * @author : zlf
     * date    : 2019/5/26
     * github  : https://github.com/mamumu
     * blog    : https://www.jianshu.com/u/281e9668a5a6
     * desc    :
     */
    public class MainAdapter extends BaseQuickAdapter<MainEntity.ResultsBean, BaseViewHolder> {
    
        private HashMap<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
        private int mWidth;
        private int mHeight;
    
        public MainAdapter(@Nullable List<MainEntity.ResultsBean> data) {
            super(R.layout.item_main, data);
        }
    
        public MainAdapter() {
            super(R.layout.item_main);
        }
    
        @Override
        protected void convert(BaseViewHolder helper, MainEntity.ResultsBean data) {
            //将每一个需要赋值的id和对应的数据绑定
            ImageView imageView = helper.getView(R.id.item_iv_main);
            helper.setText(R.id.item_tv_main_name, "type:"+data.getType()+"+id:"+data.get_id());//名字
            if (TextUtils.isEmpty(data.getType()) || TextUtils.isEmpty(data.getUrl())) {
                return;
            }
            if (hashMap.get(helper.getAdapterPosition()) != null) {
                //屏幕的宽度(px值)
                int screenWidth = getContext().getResources().getDisplayMetrics().widthPixels;
                //图片的宽度
                mWidth = screenWidth / 2;
                //图片的高度
                mHeight=hashMap.get(helper.getAdapterPosition());
                //如果图片存储的高度不为空,则使用图片的存储高度作为imageView的高度
                ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
                layoutParams.width=mWidth;
                layoutParams.height=mHeight;
                imageView.setLayoutParams(layoutParams);
                Log.d("mmmm", "S_height" + helper.getAdapterPosition() + "=" + hashMap.get(helper.getAdapterPosition()));
            }
    
            RequestOptions options = new RequestOptions()
                    .diskCacheStrategy(DiskCacheStrategy.ALL)
                    .placeholder(R.mipmap.icon_no_shop)
                    .error(R.mipmap.icon_no_shop);
            Glide.with(mContext)
                    .asBitmap()
                    .apply(options)
                    .addListener(new RequestListener<Bitmap>() {
                        @Override
                        public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Bitmap> target, boolean isFirstResource) {
                            return false;
                        }
    
                        @Override
                        public boolean onResourceReady(Bitmap bitmap, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
                            //存储图片的高度,以便在向上滚动的时候使用
                            int height = bitmap.getHeight();
                            if (hashMap.get(helper.getAdapterPosition()) == null) {
                                hashMap.put(helper.getAdapterPosition(), height);
                            }
    //                        Log.d("mmm", "S_width" + helper.getAdapterPosition() + "=" + width); //400px
    //                        Log.d("mmm", "S_height" + helper.getAdapterPosition() + "=" + height); //400px
                            return false;
                        }
                    })
                    .load(data.getUrl())
                    .into(imageView);
            //对两个按钮进行监听
    
        }
    }
    
    

    4,对应github地址

    demo地址:https://github.com/mamumu/jsRecyclerView8

    5,本系列第一篇文章地址,如果本文看不懂可以看第一篇,如有其它疑问请留言。

    地址:https://www.jianshu.com/p/ce972355c71d

    如果有发现错误欢迎指正我及时修改,如果有好的建议欢迎留言。如果觉得对你有帮助欢迎给小星星,谢谢。

    相关文章

      网友评论

        本文标题:Android中RecyclerView配合BaseRecycl

        本文链接:https://www.haomeiwen.com/subject/uamqmctx.html