当前位置: 首页 > news >正文

网站报备之后如何建设网站做网站购买虚拟主机送模板吗

网站报备之后如何建设网站,做网站购买虚拟主机送模板吗,wordpress中英双语选择,网站地址格式前言 网上充满着不完善的基于RecyclerView的瀑布流实现#xff0c;要么根本是错的、要么就是只知其一不知其二、要么就是一充诉了一堆无用代码、要么用的是古老的MVC设计模式。 一个真正的、用户体验类似于淘宝、抖音的瀑布流怎么实现目前基本为无解。因为本人正好自己空闲时也…前言 网上充满着不完善的基于RecyclerView的瀑布流实现要么根本是错的、要么就是只知其一不知其二、要么就是一充诉了一堆无用代码、要么用的是古老的MVC设计模式。 一个真正的、用户体验类似于淘宝、抖音的瀑布流怎么实现目前基本为无解。因为本人正好自己空闲时也在做前后台一体化开发所以直接从本人自己项目里mao-sir.com公开一部分核心代码以便于加快大家的学习进度以使得各位不要在这种小东西上折腾太多时间。 什么是瀑布流 注意看这边的这个布局大家有没有发觉这些照片分成左右、最多的还有分成三列的但是每一行的照片、视频、内容的高度是“错开”的。最早采用此布局的网站是Pinterest逐渐在国内流行开来。国内大多数做的好的大厂的APP都是这种布局、尤以UGCUGC它的中文意思是用户生产内容的意思简称为UGC为主的APP采用此布局最多像知乎上的精品贴、推荐贴、小红书种草等都是这种风格。 瀑布流布局的优点为 吸引用户当用户在浏览瀑布流式布局的时候这里抛开懒加载用户会产生一种错觉就是信息是不停的在更新的这会激发用户的好奇心使用户不停的往下滑动。良好视觉体验采用瀑布流布局的方式可以打破常规网站布局排版给用户眼前一亮的新鲜感用户在浏览内容时会感到很有新鲜感带来良好的视觉体验。更好的适应移动端由于移动设备屏幕比电脑小一个屏幕显示的内容不会非常多因此可能要经常翻页。而在建网站时使用瀑布流布局用户则只需要进行滚动就能够不断浏览内容。这一点和懒加载有一点像 怎么实现瀑布流 网上有一些第三方控件使用了瀑布流但是这些第三方控件都已经废弃或者是停更了。这些第三方控件本人都用过不是有各种BUG就是把问题搞了很复杂。这东西其实很简单一天内就可以做出生产级别的应用了哪有这么难。 难就是难在太多初学者为了赶项目或者说很多人急功近利只想着copy paste因此搞了一堆其实无用的代码还把问题“混搅”了。 因此本篇会从本质上还原最最干净的瀑布流同时本人会基于mvvm设计模式来讲这个瀑布流。我们用的就是Android原生的控件RecyclerView组件。下面是类淘宝、抖音用户极致体验的一个瀑布流“正例”我们在文未还会给出一个体验不好的反例供读者对比。 androidx.recyclerview.widget.RecyclerViewandroid:idid/rvandroid:layout_widthmatch_parentandroid:layout_heightwrap_content/androidx.recyclerview.widget.RecyclerView 下面我们来看实现 。 基于MVVM设计模式的RecyclerView实现瀑布流代码 工程整体结构 这是一个使用androidx的基于mvvm的工程。 至于如何把一个工程变成androidx和mvvm此处就不再赘述了在我前面的博客中已经写了很详细了。 布局 activity_main.xml布局 ?xml version1.0 encodingutf-8? layout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoxmlns:toolshttp://schemas.android.com/toolsdata/dataLinearLayoutandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:orientationverticaltools:context.MainActivityandroidx.recyclerview.widget.RecyclerViewandroid:idid/rvandroid:layout_widthmatch_parentandroid:layout_heightwrap_content/androidx.recyclerview.widget.RecyclerView/LinearLayout /layout 瀑布流中具体的明细布局-rv_item.xml 在明细布局里整体瀑布流墙就两个元素一个是照片的url另一个是文本框实现很简单。 ?xml version1.0 encodingutf-8? layout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autodatavariablenameitemtypeorg.mk.android.demo.demo.staggerdrecyclerview.RVBean //dataLinearLayoutandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroidx.cardview.widget.CardViewandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:layout_margin8dpapp:cardCornerRadius8dpapp:cardElevation4dpLinearLayoutandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:orientationverticalImageViewandroid:idid/rvImageViewandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:adjustViewBoundstrueandroid:scaleTypefitXYapp:url{item.url} /TextViewandroid:idid/rvTextViewandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:textAlignmentcenterandroid:layout_margin4dpandroid:text{item.text} //LinearLayout/androidx.cardview.widget.CardView/LinearLayout /layout现在就来看我们的代码。 后端代码 RVBean.java package org.mk.android.demo.demo.staggerdrecyclerview;import android.util.Log; import android.widget.ImageView;import androidx.databinding.BindingAdapter;import com.bumptech.glide.Glide;import java.util.Objects;public class RVBean {private String url;private String text;private final static String TAG DemoStaggerdRecyclerView;BindingAdapter(url)public static void loadImg(ImageView imageView, String url) {Glide.with(imageView).load(url).into(imageView);}public String getUrl() {return url;}public void setUrl(String url) {this.url url;}public String getText() {return text;}public void setText(String text) {this.text text;}public RVBean(String url, String text) {this.url url;this.text text;}Overridepublic boolean equals(Object o) {if (this o) {//Log.i(TAG, thiso return true);return true;}if (o null || getClass() ! o.getClass()) {//Log.i(TAG, onull||getClass()!o.getClass() is false);return false;}RVBean rvBean (RVBean) o;if (rvBean.url.length() ! url.length() || rvBean.text.length() ! text.length()) {//Log.i(TAG, target length()!existed url length);return false;}if(url.equals(rvBean.url)text.equals(rvBean.text)){//Log.i(TAG,url euqlas text equals);return true;}else{//Log.i(TAG,not url euqlas text equals);return false;}}Overridepublic int hashCode() {int hashCode Objects.hash(url, text);//Log.i(TAG, hashCode- hashCode);return hashCode;} }代码中它定义了两个元素一个为文本框一个为用于加载网络图片的url网络图片我用的是我另一台VM上的Nginx做的静态图片资源服务。 RVAdapter.java package org.mk.android.demo.demo.staggerdrecyclerview;import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView;import androidx.annotation.NonNull; import androidx.databinding.DataBindingUtil; import androidx.recyclerview.widget.RecyclerView;import com.bumptech.glide.Glide;import org.mk.android.demo.demo.staggerdrecyclerview.databinding.RvItemBinding;import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Locale;public class RVAdapter extends RecyclerView.AdapterRVAdapter.VH {private Context context;private ListRVBean rvBeans;private final static String TAG DemoStaggerdRecyclerView;public RVAdapter(Context context, ListRVBean rvBeans) {this.context context;this.rvBeans rvBeans;}Overridepublic int getItemViewType(int position) {return position;}NonNullOverridepublic VH onCreateViewHolder(NonNull ViewGroup parent, int viewType) {return new VH(DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.rv_item, parent, false).getRoot());}Overridepublic void onBindViewHolder(NonNull VH holder, int position) {//try {RvItemBinding binding DataBindingUtil.bind(holder.itemView);//binding.rvTextView.setText(rvBeans.get(position).getText());binding.setItem(rvBeans.get(position));/*//Set sizeBitmapFactory.Options options new BitmapFactory.Options();options.inJustDecodeBounds true;//这个参数设置为true才有效Bitmap bmp BitmapFactory.decodeFile(rvBeans.get(position).getUrl(), options);//这里的bitmap是个空int outHeight options.outHeight;int outWidth options.outWidth;Glide.with(context).load(rvBeans.get(position).getUrl()).override(outWidth,outHeight).into(binding.rvImageView);} catch (Exception e) {Log.e(TAG, onbindViewHolder error: e.getMessage(), e);}*/}Overridepublic int getItemCount() {return rvBeans.size();}public class VH extends RecyclerView.ViewHolder {public VH(NonNull View itemView) {super(itemView);}}//增加外部调用增加一条记录public void refreshDatas(ListRVBean datas) {int pc0;if (datas ! null datas.size() 0) {int oldSize rvBeans.size();//ListRVBean refreshedData new ArrayListRVBean();boolean isItemExisted false;for (IteratorRVBean newData datas.iterator(); newData.hasNext(); ) {RVBean a newData.next();for (IteratorRVBean existedData rvBeans.iterator(); existedData.hasNext(); ) {RVBean b existedData.next();if (b.equals(a)) {{isItemExisted true;//Log.i(TAG, b.getText() - b.getUrl() is existed);break;}}}if (!isItemExisted) {pc1;rvBeans.add(a);}}Log.i(TAG,pc-pc);if(pc0){notifyItemRangeChanged(oldSize,rvBeans.size());}}} }核心代码导读 这个adapter用的正是mvvm设计模式做的adapter这个adapter和网上那些错误、有坑的例子最大的不同在于getItemViewType方法内必须返回position否则你的瀑布流在上划加载新数据时会产生界面内对照片重新进行左右切换、重排、或者把照片底部留出很大一块空白如左边垂直排3张右边一大边空白或者反之亦然的情况必须使用notifyItemRangeChanged来通知刷新新数据网上很多例子用的是notifyDataSetChange或者是其它相关的notify它们都是错的这是因为RecyclerViewer在上划下划时会导致整个瀑布流重新布局、而RecyclerView里用的是Glide异步加载网络图片的这会导致组件看到有一个组片就去开始计算它的高度而实际这个照片还未加载好因此才会导致RecyclerView在上划下划时整体布局重新刷新和重布局。一定记得这个notifyItemRangeChanged同时这个方法在使用前加载所有的图片list数据传参有两个参数参数1加载新数据前原数据行.size()参数2新加载数据.size()有了adapter我们来看我们的应用了。 应用前先别急我们自定义了一个StaggeredGridLayoutManager。 自定义FullyStaggeredGridLayoutManager 这边先说一下为什么要自定义这个StaggeredGridLayoutManager public class FullyStaggeredGridLayoutManager extends StaggeredGridLayoutManager { 大家可以认为这个类是一个策略。这也是网上绝大部分教程根本不说的这个策略就是从根本上避免RecyclerViewer在上划下划时不要进行左右切换、重新布局、图片闪烁以及“解决Scrollview中嵌套RecyclerView实现瀑布流时无法显示的问题同时修复了子View显示时底部多出空白区域的问题”用的我在代码中也做了注释。 此处要敲一下黑板了。因此整个RecyclerView要做到类淘宝、抖音的这种用户体验必须是adapter里的代码和这个自定义StaggeredGridLayoutManager结合起来才能做到。 因此我们下面就来看在MainActivity里如何把adapter结合着这个自定义的StaggeredGridLayoutManager的应用吧。 先上我们自定义的这个StaggeredGridLayoutManager-我们在此把它命名叫做FullyStaggeredGridLayoutManager的全代码。 FullyStaggeredGridLayoutManager.java代码 package org.mk.android.demo.demo.staggerdrecyclerview;import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.View;import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.StaggeredGridLayoutManager;import java.lang.reflect.Field;/*** descride 解决Scrollview中嵌套RecyclerView实现瀑布流时无法显示的问题同时修复了子View显示时底部多出空白区域的问题*/ public class FullyStaggeredGridLayoutManager extends StaggeredGridLayoutManager {private static boolean canMakeInsetsDirty true;private static Field insetsDirtyField null;private static final int CHILD_WIDTH 0;private static final int CHILD_HEIGHT 1;private static final int DEFAULT_CHILD_SIZE 100;private int spanCount 0;private final int[] childDimensions new int[2];private int[] childColumnDimensions;private int childSize DEFAULT_CHILD_SIZE;private boolean hasChildSize;private final Rect tmpRect new Rect();public FullyStaggeredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}public FullyStaggeredGridLayoutManager(int spanCount, int orientation) {super(spanCount, orientation);this.spanCount spanCount;}public static int makeUnspecifiedSpec() {return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);}Overridepublic void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec,int heightSpec) {final int widthMode View.MeasureSpec.getMode(widthSpec);final int heightMode View.MeasureSpec.getMode(heightSpec);final int widthSize View.MeasureSpec.getSize(widthSpec);final int heightSize View.MeasureSpec.getSize(heightSpec);final boolean hasWidthSize widthMode ! View.MeasureSpec.UNSPECIFIED;final boolean hasHeightSize heightMode ! View.MeasureSpec.UNSPECIFIED;final boolean exactWidth widthMode View.MeasureSpec.EXACTLY;final boolean exactHeight heightMode View.MeasureSpec.EXACTLY;final int unspecified makeUnspecifiedSpec();if (exactWidth exactHeight) {// in case of exact calculations for both dimensions lets use default onMeasure implementationsuper.onMeasure(recycler, state, widthSpec, heightSpec);return;}final boolean vertical getOrientation() VERTICAL;initChildDimensions(widthSize, heightSize, vertical);int width 0;int height 0;// its possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This// happens because their invalidation happens after onMeasure method. As a workaround lets clear the// recycler now (it should not cause any performance issues while scrolling as onMeasure is never// called whiles scrolling)recycler.clear();final int stateItemCount state.getItemCount();final int adapterItemCount getItemCount();childColumnDimensions new int[adapterItemCount];// adapter always contains actual data while state might contain old data (f.e. data before the animation is// done). As we want to measure the view with actual data we must use data from the adapter and not from the// statefor (int i 0; i adapterItemCount; i) {if (vertical) {if (!hasChildSize) {if (i stateItemCount) {// we should not exceed state count, otherwise well get IndexOutOfBoundsException. For such items// we will use previously calculated dimensionsmeasureChild(recycler, i, widthSize, unspecified, childDimensions);} else {logMeasureWarning(i);}}childColumnDimensions[i] childDimensions[CHILD_HEIGHT];//height childDimensions[CHILD_HEIGHT];if (i 0) {width childDimensions[CHILD_WIDTH];}if (hasHeightSize height heightSize) {break;}} else {if (!hasChildSize) {if (i stateItemCount) {// we should not exceed state count, otherwise well get IndexOutOfBoundsException. For such items// we will use previously calculated dimensionsmeasureChild(recycler, i, unspecified, heightSize, childDimensions);} else {logMeasureWarning(i);}}width childDimensions[CHILD_WIDTH];if (i 0) {height childDimensions[CHILD_HEIGHT];}if (hasWidthSize width widthSize) {break;}}}int[] maxHeight new int[spanCount];for (int i 0; i adapterItemCount; i) {int position i % spanCount;if (i spanCount) {maxHeight[position] childColumnDimensions[i];} else if (position spanCount) {int mixHeight maxHeight[0];int mixPosition 0;for (int j 0; j spanCount; j) {if (mixHeight maxHeight[j]) {mixHeight maxHeight[j];mixPosition j;}}maxHeight[mixPosition] childColumnDimensions[i];}}for (int i 0; i spanCount; i) {for (int j 0; j spanCount - i - 1; j) {if (maxHeight[j] maxHeight[j 1]) {int temp maxHeight[j];maxHeight[j] maxHeight[j 1];maxHeight[j 1] temp;}}}height maxHeight[0];//this is max heightif (exactWidth) {width widthSize;} else {width getPaddingLeft() getPaddingRight();if (hasWidthSize) {width Math.min(width, widthSize);}}if (exactHeight) {height heightSize;} else {height getPaddingTop() getPaddingBottom();if (hasHeightSize) {height Math.min(height, heightSize);}}setMeasuredDimension(width, height);}private void logMeasureWarning(int child) {if (BuildConfig.DEBUG) {Log.w(LinearLayoutManager, Cant measure child # child , previously used dimensions will be reused. To remove this message either use #setChildSize() method or dont run RecyclerView animations);}}private void initChildDimensions(int width, int height, boolean vertical) {if (childDimensions[CHILD_WIDTH] ! 0 || childDimensions[CHILD_HEIGHT] ! 0) {// already initialized, skippingreturn;}if (vertical) {childDimensions[CHILD_WIDTH] width;childDimensions[CHILD_HEIGHT] childSize;} else {childDimensions[CHILD_WIDTH] childSize;childDimensions[CHILD_HEIGHT] height;}}Override public void setOrientation(int orientation) {// might be called before the constructor of this class is called//noinspection ConstantConditionsif (childDimensions ! null) {if (getOrientation() ! orientation) {childDimensions[CHILD_WIDTH] 0;childDimensions[CHILD_HEIGHT] 0;}}super.setOrientation(orientation);}public void clearChildSize() {hasChildSize false;setChildSize(DEFAULT_CHILD_SIZE);}public void setChildSize(int childSize) {hasChildSize true;if (this.childSize ! childSize) {this.childSize childSize;requestLayout();}}private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize,int heightSize, int[] dimensions) {final View child;try {child recycler.getViewForPosition(position);} catch (IndexOutOfBoundsException e) {if (BuildConfig.DEBUG) {Log.w(LinearLayoutManager,LinearLayoutManager doesnt work well with animations. Consider switching them off,e);}return;}final RecyclerView.LayoutParams p (RecyclerView.LayoutParams) child.getLayoutParams();final int hPadding getPaddingLeft() getPaddingRight();final int vPadding getPaddingTop() getPaddingBottom();final int hMargin p.leftMargin p.rightMargin;final int vMargin p.topMargin p.bottomMargin;// we must make insets dirty in order calculateItemDecorationsForChild to workmakeInsetsDirty(p);// this method should be called before any getXxxDecorationXxx() methodscalculateItemDecorationsForChild(child, tmpRect);final int hDecoration getRightDecorationWidth(child) getLeftDecorationWidth(child);final int vDecoration getTopDecorationHeight(child) getBottomDecorationHeight(child);final int childWidthSpec getChildMeasureSpec(widthSize, hPadding hMargin hDecoration, p.width,canScrollHorizontally());final int childHeightSpec getChildMeasureSpec(heightSize, vPadding vMargin vDecoration, p.height,canScrollVertically());child.measure(childWidthSpec, childHeightSpec);dimensions[CHILD_WIDTH] getDecoratedMeasuredWidth(child) p.leftMargin p.rightMargin;dimensions[CHILD_HEIGHT] getDecoratedMeasuredHeight(child) p.bottomMargin p.topMargin;// as view is recycled lets not keep old measured valuesmakeInsetsDirty(p);recycler.recycleView(child);}private static void makeInsetsDirty(RecyclerView.LayoutParams p) {if (!canMakeInsetsDirty) {return;}try {if (insetsDirtyField null) {insetsDirtyField RecyclerView.LayoutParams.class.getDeclaredField(mInsetsDirty);insetsDirtyField.setAccessible(true);}insetsDirtyField.set(p, true);} catch (NoSuchFieldException e) {onMakeInsertDirtyFailed();} catch (IllegalAccessException e) {onMakeInsertDirtyFailed();}}private static void onMakeInsertDirtyFailed() {canMakeInsetsDirty false;if (BuildConfig.DEBUG) {Log.w(LinearLayoutManager,Cant make LayoutParams insets dirty, decorations measurements might be incorrect);}} } MainActivity.java 从这儿开始我们要进入正题了这边要说真正的RecyclerView的应用了。 在此演示代码块里为了同时便于初学者和正在寻找RecyclerView上划下划时错位过大、重新布局影响体验的老手同时阅读和查询问题时方便我要说一下整个Demo代码运行的设计思路如下 上手先加载12条数据对上划手指按住屏幕向上拉一直拉、拉、拉拉到第12条触发了RecyclerView的onScrollStateChanged中的“往上拉拉不动”事件后开始再加载6条数据以模拟实际项目中的“翻页时走一下后台API取新数据”的过程 package org.mk.android.demo.demo.staggerdrecyclerview;import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.SimpleItemAnimator; import androidx.recyclerview.widget.StaggeredGridLayoutManager;import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater;import org.mk.android.demo.demo.staggerdrecyclerview.databinding.ActivityMainBinding;import java.util.ArrayList; import java.util.List;public class MainActivity extends AppCompatActivity {private ActivityMainBinding binding;private ListRVBean rvBeanList new ArrayList();private RVAdapter adapter;private final static String TAG DemoStaggerdRecyclerView;private final static String CDN_URLhttp://172.16.4.249/mkcdn;private FullyStaggeredGridLayoutManager slmnull;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.activity_main, null, false);setContentView(binding.getRoot());slmnew FullyStaggeredGridLayoutManager(2,FullyStaggeredGridLayoutManager.VERTICAL);binding.rv.setLayoutManager(slm);((SimpleItemAnimator)binding.rv.getItemAnimator()).setSupportsChangeAnimations(false);((DefaultItemAnimator) binding.rv.getItemAnimator()).setSupportsChangeAnimations(false);binding.rv.getItemAnimator().setChangeDuration(0);binding.rv.setHasFixedSize(true);initData();}private void initData() {rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_13.jpeg, 1));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_14.jpeg, 2));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_15.jpeg, 3));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_16.jpeg, 4));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_17.jpeg, 5));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_18.jpeg, 6));rvBeanList.add(new RVBean(CDN_URLimg/recommend/recom_19.jpeg, 7));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_20.jpeg, 8));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_21.jpeg, 9));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_22.jpeg, 10));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_23.jpeg, 11));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_24.jpeg, 12));adapter new RVAdapter(this, rvBeanList);//主要就是这个LayoutManager就是用这个来实现瀑布流的2表示有2列(垂直)或3行(水平)我们这里用的垂直VERTICAL//binding.rv.addItemDecoration(new SpaceItemDecoration(2, 20));binding.rv.addOnScrollListener(new RecyclerView.OnScrollListener() {Overridepublic void onScrollStateChanged(NonNull RecyclerView recyclerView, int newState) {super.onScrollStateChanged(recyclerView, newState);if (!recyclerView.canScrollVertically(1) newState RecyclerView.SCROLL_STATE_IDLE) {Log.i(TAG, 上拉拉不动时触发加载新数据);rvBeanList new ArrayList();rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_25.jpeg, 13));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_26.jpeg, 14));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_27.jpeg, 15));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_28.jpeg, 16));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_29.jpeg, 17));rvBeanList.add(new RVBean(CDN_URL/img/recommend/recom_30.jpeg, 18));adapter.refreshDatas(rvBeanList);}if (!recyclerView.canScrollVertically(-1) newState RecyclerView.SCROLL_STATE_IDLE) {Log.i(TAG, 下拉拉不动时触发加载新数据);}}Overridepublic void onScrolled(RecyclerView recyclerView, int dx, int dy) {super.onScrolled(recyclerView, dx, dy);slm.invalidateSpanAssignments();//防止第一行到顶部有空白}});//((SimpleItemAnimator)RecyclerView.getItemAnimator()).setSupportsChangeAnimations(false);binding.rv.setAdapter(adapter);} } 核心代码导读 以下这一陀就是我说的使用自定义的StaggeredGridLayoutManageradapter中的事件覆盖一起实现了著名的RecyclerView上划下划时重新布局、翻页的梗。 同时一定要记得覆盖RecyclerView的onScrolled事件在事件中加入这样的代码 Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {super.onScrolled(recyclerView, dx, dy);slm.invalidateSpanAssignments();//防止第一行到顶部有空白 } 总结 正确的做法 因此这边总结一下共有5个点做到这5个点才能真正避免网上一堆的关于RecyclerView快速上划下划时整个界面产生了春左右列切换、重新布局、布局不合理如左边垂直排了3-4个图片而把右边留出一大块空白的梗的综合手段 必须使用一个自定义的StaggeredGridLayoutManager你可以直接使用我博客中的代码它来自于我正在制作的上生产的mao-sir.com的app中的代码这是我的个人的一个开源中台产品必须要设置上面代码截图中的4个属性即 SimpleItemAnimator里的setSupportsChangeAnimations(false); DefaultItemAnimator里的setSupportsChangeAnimations(false); setChangeDuration(0); setHasFixedSize(true); 必须要覆盖RecyclerView的onScrolled方法在方法里设置防止第一行到顶部有空白的操作 必须要在adapter里覆盖getItemViewType在方法内返回position 必须要在adapter里刷新数据时使用notifyItemRangeChanged 错误的做法 这边我说网上绝大部分示例是错的可能是客气了点因该说可以搜到的全是错的。我们也来总结一下希望各位不要再去踩这种坑了。 把图片的尺寸预先存在后台、每次接口取图片时后台把这个尺寸返回给到RecyclerView的adapter。。。这是得有多。。。无聊的做法覆盖一个getItemViewType不就同样实现了这样的手法setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE)的做法是错的设完后图片要么在一开始进入界面时显示不出要么显示不全我不知道这个问题最早是谁想出的解决办法怎么自己也不去验证一下对不对网上还有一种手法也是错的离谱就是在onBindViewHolder方法里通过BitMapGLIDE重设图片的尺寸这种方法根本无效下面我们来感受一下使用了网上错误的手法后这个瀑布流会变成什么样大家就能感受到我说的这种体验问题了。这种体验问题在APP上如果没有做好会直接要了你这家企业的“口碑的命”。 来看错误手法导致的差劲的瀑布流体验 请看下面的“反例”的演示注意到了上划或者下划时左右列切换、重排的问题了没给大家感受一下。 后记 此处我们需要写一下RecyclerView在生产上真正使用时在设计上的一个补充点。 根据上面的代码我们可以看到我用了上划触发上划到一页底部并实现翻页的无缝衔接的体验。 但实际在生产上我们会遇到上千近万张图片的前后台传输。如果图片一多、翻页一多因为这个翻而是介于前后台交互的一个动作因此有时在遇有弱网或者是网络抖动时卡死Android主进程并导致App闪退、卡屏、白屏等现象。 所以我这边给出万级QPS并发下的瀑布流设计上的两个点 APP的首页、分类、搜索的list、瀑布流、三行三列这种界面的数据必须来自后台的es而不是去什么后台查数据库这么来一下。在中/后台我们通过企业内部的运营管理平台对于内容、商品进行“上下架”后会触发一个ES或者是RediSearch我的开源产品里已经彻底不用ES了的全量或者是增量索引在APP端不能翻一页就去后台取一下而是APP端也要做一层“缓存”可以把已经获取到的数据存在本地的内嵌存储本地取不到再去取后/中台的API接口获取数据以此充分极大化加快我们APP端的反映速度同时取得最优的用户体验。 好结束今天的教程。大家不妨自己动一下手试试看效果去吧。
http://www.dnsts.com.cn/news/150596.html

相关文章:

  • 网站模板类型网站设计的公司怎么样
  • 天津黑曼巴网站建设网页设计尺寸分辨率
  • 甜品网站模板代码手机网页游戏开发
  • 营销神器湖南网站优化
  • 有了虚拟主机怎么做网站cosy主题wordpress
  • 最便宜的网站空间电子商务岗位有哪些
  • 上海企业网站制作哪家好python做网站的好处
  • 网站风格类型有哪些互联网推广图片
  • 织梦网站栏目添加产品网络推广方案设计
  • 花样云做网站怎样济源建设网站
  • 网页设计与制作的实训报告seo优化费用
  • 局网站建设申请网站支付体现功能怎么做
  • 免费空间网站源码网站前台 后台
  • 做设计的素材网站有哪些网站开发主要技术
  • 天津企业网站专业订制网站开发需要用哪些东西
  • 网站编写教程青岛抖音广告
  • 做落地页素材在什么网站上找wordpress标签工具栏
  • 耒阳市做网站的如何做电影网站赚钱
  • 连云港做网站设计西安市住房和城乡建设局
  • 天津个人网站建设怎么让网站被收录
  • 国外交互设计网站欣赏网站建设公司山而
  • 湖南营销型网站建设 干净磐石网络网页制作工具按其制作方式分可以分为哪几种
  • 如何做国外网站推广小程序设计流程
  • 英文网站怎么做推广门户网站维护方案
  • wordpress 设置404页面孝感网站seo
  • 宁波seo公司网站推广夫唯seo视频教程
  • wordpress菜单显示选项打不开关键词seo排名怎么样
  • 我的网站域名马铃薯交易网站建设方案
  • asp手机网站源码下载网页制作需要学多久
  • 如何创建网站的快捷方式crm管理是什么意思