Android 一分钟使用RecyclerView完美实现瀑布

【免费】安卓RecyclerView瀑布流效果实现资源-CSDN文库

1.WaterfallFlowActivity 主函数代码: 

package com.example.mytestapplication;import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.Toast;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 com.example.mytestapplication.databinding.ActivityMainBinding;
import com.example.mytestapplication.databinding.ActivityTestBinding;
import com.example.mytestapplication.databinding.ActivityWaterfallFlowBinding;
import com.example.mytestapplication.pbl.FullyStaggeredGridLayoutManager;
import com.example.mytestapplication.pbl.RVAdapter;
import com.example.mytestapplication.pbl.RVBean;
import com.example.mytestapplication.ui.DemoAdapter;
import com.example.mytestapplication.ui.ImageUtil;import java.util.ArrayList;
import java.util.List;/*** RecyclerView实现简单的瀑布流效果*/
public class WaterfallFlowActivity extends AppCompatActivity {private ActivityWaterfallFlowBinding binding;private List<RVBean> rvBeanList = new ArrayList<>();private RVAdapter adapter;private final static String TAG = "DemoStaggerdRecyclerView";private final static String CDN_URL="https://vd3.bdstatic.com/mda-pehiqe0dcmd4cry9/sc/cae_h264/1684592473466216903/mda-pehiqe0dcmd4cry9.mp4";private final static String CDN_URL1="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F70c03ac511079c42b2ecddef0ff4444f846de67ebf58a-ZQ9m0f_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641973590&t=2c23fb6d6b200160666f0ccd81d7368a";private final static String CDN_URL2="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fa3652914-9074-4c2d-ba91-7677c42a0cdf%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1687673320&t=ace2227ee8b9c0ac0aadaf49cbe25f1e";private final static String CDN_URL3="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F5c2fde87-cfa6-4e76-893e-3f032cc41ce5%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1687441411&t=10b8d72201811e966f1404457a5371d2";private final static String CDN_URL4 = "https://img.alicdn.com/tps/TB1uyhoMpXXXXcLXVXXXXXXXXXX-476-538.jpg_240x5000q50.jpg_.webp";private final static String CDN_URL5 = "http://b247.photo.store.qq.com/psb?/V11ZojBI312o2K/63aY8a4M5quhi.78*krOo7k3Gu3cknuclBJHS3g1fpc!/b/dDXWPZMlBgAA";private final static String CDN_URL6 = "https://img0.baidu.com/it/u=2746042376,2078414564&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=433";private final static String CDN_URL7 = "https://s1.chu0.com/src/img/gif/52/52784937bc374c779332150318a75baf.gif?imageMogr2/auto-orient/thumbnail/!231x201r/gravity/Center/crop/231x201/quality/85/&e=1735488000&token=1srnZGLKZ0Aqlz6dk7yF4SkiYf4eP-YrEOdM1sob:SXbWiapi5fG0MM5V-0hwE43cvnY=";private final static String CDN_URL8 = "https://s1.chu0.com/src/img/gif/60/606e2efad8ea4417a4e101fa1285d609.gif?e=1735488000&token=1srnZGLKZ0Aqlz6dk7yF4SkiYf4eP-YrEOdM1sob:IA5gbzlKc-NNfpArFhy-5xGKjUg=";private final static String CDN_URL9 = "http://contentcms-bj.cdn.bcebos.com/cmspic/dd7b0d8aa276e3a062edf462b4082065.jpeg";private final static String CDN_URL10 = "";private final static String CDN_URL11 = "";private final static String CDN_URL12 = "";private final static String CDN_URL13 = "";private final static String CDN_URL14 = "";private FullyStaggeredGridLayoutManager slm=null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.activity_waterfall_flow, null, false);setContentView(binding.getRoot());slm=new 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, "1"));rvBeanList.add(new RVBean(CDN_URL1, "2"));rvBeanList.add(new RVBean(CDN_URL2, "3"));rvBeanList.add(new RVBean(CDN_URL3, "4"));rvBeanList.add(new RVBean(CDN_URL4, "5"));rvBeanList.add(new RVBean(CDN_URL1, "6"));rvBeanList.add(new RVBean(CDN_URL, "7"));rvBeanList.add(new RVBean(CDN_URL1, "8"));rvBeanList.add(new RVBean(CDN_URL, "9"));rvBeanList.add(new RVBean(CDN_URL1, "10"));rvBeanList.add(new RVBean(CDN_URL, "11"));rvBeanList.add(new RVBean(CDN_URL8, "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, "13"));rvBeanList.add(new RVBean(CDN_URL1, "14"));rvBeanList.add(new RVBean(CDN_URL, "15"));rvBeanList.add(new RVBean(CDN_URL1, "16"));rvBeanList.add(new RVBean(CDN_URL, "17"));rvBeanList.add(new RVBean(CDN_URL1, "18"));rvBeanList.add(new RVBean(CDN_URL6, "19"));rvBeanList.add(new RVBean(CDN_URL6, "20"));rvBeanList.add(new RVBean(CDN_URL7, "21"));rvBeanList.add(new RVBean(CDN_URL8, "22"));rvBeanList.add(new RVBean(CDN_URL, "23"));rvBeanList.add(new RVBean(CDN_URL, "24"));rvBeanList.add(new RVBean(CDN_URL1, "25"));rvBeanList.add(new RVBean(CDN_URL, "26"));rvBeanList.add(new RVBean(CDN_URL1, "27"));rvBeanList.add(new RVBean(CDN_URL, "28"));rvBeanList.add(new RVBean(CDN_URL1, "29"));rvBeanList.add(new RVBean(CDN_URL1, "30"));rvBeanList.add(new RVBean(CDN_URL6, "31"));rvBeanList.add(new RVBean(CDN_URL7, "32"));rvBeanList.add(new RVBean(CDN_URL8, "33"));rvBeanList.add(new RVBean(CDN_URL1, "34"));rvBeanList.add(new RVBean(CDN_URL6, "35"));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);//使用接口实现对应子控件中的监听adapter.setOnCardItemClickListener(new RVAdapter.CardListener() {@Overridepublic void setCardClickListener(int num) {Toast.makeText(WaterfallFlowActivity.this, "这是第"+num+"个ITEM", Toast.LENGTH_SHORT).show();}});}}

2.适配器:

package com.example.mytestapplication.pbl;import android.annotation.SuppressLint;
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 com.example.mytestapplication.R;
import com.example.mytestapplication.databinding.RvItemBinding;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;public class RVAdapter extends RecyclerView.Adapter<RVAdapter.VH> {private Context context;private List<RVBean> rvBeans;CardListener cardListener;private final static String TAG = "DemoStaggerdRecyclerView";public RVAdapter(Context context, List<RVBean> rvBeans) {this.context = context;this.rvBeans = rvBeans;}@Overridepublic int getItemViewType(int position) {return position;}@NonNull@Overridepublic 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, @SuppressLint("RecyclerView") 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);}*/binding.cardView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(cardListener != null){cardListener.setCardClickListener(position);}}});}@Overridepublic int getItemCount() {return rvBeans.size();}public class VH extends RecyclerView.ViewHolder {public VH(@NonNull View itemView) {super(itemView);}}//增加外部调用增加一条记录public void refreshDatas(List<RVBean> datas) {int pc=0;if (datas != null && datas.size() > 0) {int oldSize = rvBeans.size();//List<RVBean> refreshedData = new ArrayList<RVBean>();boolean isItemExisted = false;for (Iterator<RVBean> newData = datas.iterator(); newData.hasNext(); ) {RVBean a = newData.next();for (Iterator<RVBean> 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) {pc+=1;rvBeans.add(a);}}Log.i(TAG,">>>>>>pc->"+pc);if(pc>0){notifyItemRangeChanged(oldSize,rvBeans.size());}}}public static interface CardListener{public void setCardClickListener(int num);}public void setOnCardItemClickListener(CardListener mCardListener) {cardListener = mCardListener;}
}

3.自定义FullyStaggeredGridLayoutManager:

package com.example.mytestapplication.pbl;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 let's 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;// it's 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 let's 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 we'll 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 we'll 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", "Can't measure child #"
//                    + child
//                    + ", previously used dimensions will be reused."
//                    + "To remove this message either use #setChildSize() method or don't 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 doesn't 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 let's 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",
//                    "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
//        }}
}

4.布局:

activity_waterfall_flow

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"><data></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv"android:layout_width="match_parent"android:layout_height="wrap_content"></androidx.recyclerview.widget.RecyclerView></LinearLayout>
</layout>

rv_item

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><data><variablename="item"type="com.example.mytestapplication.pbl.RVBean" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><androidx.cardview.widget.CardViewandroid:id="@+id/cardView"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="8dp"app:cardCornerRadius="8dp"app:cardElevation="4dp"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><ImageViewandroid:id="@+id/rvImageView"android:layout_width="match_parent"android:layout_height="wrap_content"android:adjustViewBounds="true"android:scaleType="fitXY"app:url="@{item.url}" /><TextViewandroid:id="@+id/rvTextView"android:layout_width="match_parent"android:layout_height="wrap_content"android:textAlignment="center"android:layout_margin="4dp"android:text="@{item.text}" /></LinearLayout></androidx.cardview.widget.CardView></LinearLayout>
</layout>

5.build.gradle

plugins {id("com.android.application")
}android {namespace = "com.example.mytestapplication"compileSdk = 33dataBinding {enable = true}defaultConfig {applicationId = "com.example.mytestapplication"minSdk = 24targetSdk = 33versionCode = 1versionName = "1.0"testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {isMinifyEnabled = falseproguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")}}compileOptions {sourceCompatibility = JavaVersion.VERSION_1_8targetCompatibility = JavaVersion.VERSION_1_8}buildFeatures {viewBinding = true}
}dependencies {implementation("androidx.appcompat:appcompat:1.6.1")implementation("com.google.android.material:material:1.8.0")implementation("androidx.constraintlayout:constraintlayout:2.1.4")implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")implementation("androidx.navigation:navigation-fragment:2.5.3")implementation("androidx.navigation:navigation-ui:2.5.3")testImplementation("junit:junit:4.13.2")androidTestImplementation("androidx.test.ext:junit:1.1.5")androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")implementation ("com.github.bumptech.glide:glide:4.12.0")implementation ("androidx.palette:palette:1.0.0")implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><!--允许Glide监视连接状态,并在用户从断开连接到已连接网络的状态。--><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><applicationandroid:allowBackup="true"android:dataExtractionRules="@xml/data_extraction_rules"android:fullBackupContent="@xml/backup_rules"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.MyTestApplication"tools:targetApi="31"><activityandroid:name=".WaterfallFlowActivity"android:exported="true"android:label="@string/app_name"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activityandroid:name=".MainActivity"android:exported="true"><intent-filter><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

记得添加网络权限!

package com.example.mytestapplication.pbl;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.getContext()).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, ">>>>>>this==o return true");return true;}if (o == null || getClass() != o.getClass()) {//Log.i(TAG, ">>>>>>o==null||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;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/231042.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git总结 | Git面试都问些啥?

什么是Git为什么要用Git等等这些相信看到该标题点进来的同学也不希望浪费时间再看一遍&#xff0c;那么直接进入主题&#xff0c;对于日常工作中常用的Git相关操作进行整理&#xff0c;一起看看吧 面试官&#xff1a;你常用的Git操作是什么? 候选人&#xff1a;git clone 面试…

59. 螺旋矩阵 II(java实现,史上最详细教程,想学会的进!!!)

今天来分享一下螺旋矩阵的解题思路及代码的实现。 题目描述如下&#xff1a; 首先拿到这道题&#xff0c;首先不要慌张&#xff0c;我们来仔细分析一下会发现并没有那么难。 首先看下边界的元素是1、2、3递增的&#xff0c;那么我们也许可以根据这一点先把边界的元素一个一个给…

【Proteus仿真】【51单片机】视力保护仪

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使LCD1602液晶&#xff0c;按键、HC-SR04超声波、PCF8591 ADC、光敏传感器、蜂鸣器、LED等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD1602显示…

【vtkWidgetRepresentation】第十四期 二维标注

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 前言 本文分享vtk中的二维标注,主要用于医学领域,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 目录 前言 1. vtkBiDimension…

漏洞复现-海康威视 NCG 联网网关 login.php 目录遍历漏漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…

Day64力扣打卡

打卡记录 方格取数&#xff08;线性DP&#xff09; import sys input sys.stdin.readline 输入样例&#xff1a; 8 2 3 13 2 6 6 3 5 7 4 4 14 5 2 21 5 6 4 6 3 15 7 2 14 0 0 0 输出样例&#xff1a; 67 n int(input()) w [[0] * (n 1) for _ in range(n 1)] while Tru…

精选硬件连通性测试工具:企业如何做出明智选择

在当今数字化的商业环境中&#xff0c;企业的硬件连通性至关重要。选择适用的硬件连通性测试工具是确保网络和设备协同工作的关键一步。本文将探讨企业在选择硬件连通性测试工具时应考虑的关键因素&#xff0c;以帮助其做出明智的决策。 1. 功能全面性&#xff1a;首要考虑因素…

Android开发——组合函数、注解与连接Android设备

1、JetPack Compose、组合函数与注解和文本修改 1、JetPack Compose&#xff1a;Jetpack Compose 是由 Google 推出的用于构建 Android 用户界面的现代化工具包。它是一个声明式的 UI 工具包&#xff0c;用于简化 Android 应用程序的用户界面设计和开发。Jetpack Compose 采用…

C语言精选练习题:(11)打印菱形

文章目录 每日一言题目思路代码结语 每日一言 Intelligence without ambition is a bird without wings. 聪明但没有抱负&#xff0c;就像没有翅膀的鸟。 题目 输入一个整数n&#xff0c;打印对应2n-1行的菱形图案&#xff0c;比如输入7&#xff0c;图案一共13行 1 …

Stable Diffusion - High-Resolution Image Synthesis with Latent Diffusion Models

Paper name High-Resolution Image Synthesis with Latent Diffusion Models Paper Reading Note Paper URL: https://arxiv.org/abs/2112.10752 Code URL: https://github.com/CompVis/latent-diffusion TL;DR 2021 年 runway 和慕尼黑路德维希马克西米利安大学出品的文…

全球移动通信(2G/3G/4G/5G)频谱分布情况

一、概述 随着通信技术的不断发展&#xff0c;全球各国都在积极推进2G、3G、4G、5G网络的建设和应用。根据FCC统计&#xff0c;目前全球移动通信频谱分布如下&#xff1a; 二、分布 &#xff08;一&#xff09;俄罗斯 2G&#xff1a;主要使用900MHz和1800MHz两个频段。其中&…

【sqli靶场】第六关和第七关通关思路

目录 前言 一、sqli靶场第六关 1.1 判断注入类型 1.2 观察报错 1.3 使用extractvalue函数报错 1.4 爆出数据库中的表名 二、sqli靶场第七关 1.1 判断注入类型 1.2 判断数据表中的字段数 1.3 提示 1.4 构造poc爆库名 1.5 构造poc爆表名 1.6 构造poc爆字段名 1.7 构造poc获取账…

【Axure RP9】动态面板使用------案例:包括轮播图和多方式登入及左侧菜单栏案例

目录 一 动态面板简介 1.1 动态面板是什么 二 轮播图 2.1 轮播图是什么 2.2 轮播图应用场景 2.3 制作实播图 三 多方式登入 3.1多方式登入是什么 3.3 多方式登入实现 四 左侧菜单栏 4.1左侧菜单栏是什么 4.2 左侧菜单栏实现 一 动态面板简介 1.1 动态面板是什么…

【LeetCode刷题笔记(5)】【Python】【盛最多水的容器】【双指针】【中等】

文章目录 盛最多水的容器算法题描述示例示例 1示例 2 提示题意拆解 解决方案&#xff1a;【双指针】运行结果复杂度分析 结束语 盛最多水的容器 盛最多水的容器 算法题描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (…

Surface pro官方镜像恢复详细图文步骤

某些小白不会弄直接找淘宝远程刷官方系统&#xff0c;还被忽悠什么原版序列号会抹掉这样的笑话&#xff0c;真的很无语 一、准备恢复优盘 1、微软官方下载中心&#xff0c;按序列号下载&#xff0c;对应镜像&#xff0c;推荐最新版&#xff0c;但一般都要有注册微软账号才能下…

微信小程序背景图片设置

问题 :微信小程序通过css:background-image引入背景图片失败 [渲染层网络层错误] pages/wode/wode.wxss 中的本地资源图片无法通过 WXSS 获取&#xff0c;可以使用网络图片&#xff0c;或者 base64&#xff0c;或者使用<image/>标签 解决方法微信小程序在使用backgroun…

QT Widget - 随便画个圆

简介 实现在界面中画一个圆, 其实目的是想画一个LED效果的圆。代码 #include <QApplication> #include <QWidget> #include <QPainter> #include <QColor> #include <QPen>class LEDWidget : public QWidget { public:LEDWidget(QWidget *pare…

机器学习 | SVM支持向量机

欲穷千里目&#xff0c;更上一层楼。 一个空间的混乱在更高维度的空间往往意味着秩序。 Machine-Learning: 《机器学习必修课&#xff1a;经典算法与Python实战》配套代码 - Gitee.com 1、核心思想及原理 针对线性模型中分类两类点的直线如何确定。这是一个ill-posed problem。…

从零开始实现分布式服务系统

文章目录 开发前言分布式模型系统图解注册中心模块基础服务模块被依赖的服务模块&#xff08;日志服务&#xff09;服务模块&#xff08;访问服务&#xff09;运行效果开发总结 开发前言 分布式系统具有高可靠性、高性能、可扩展性、灵活性、数据共享、可靠性和地理分布等优点…

【计算机网络】TCP协议——3. 可靠性策略效率策略

前言 TCP是一种可靠的协议&#xff0c;提供了多种策略来确保数据的可靠性传输。 可靠并不是保证每次发送的数据&#xff0c;对方都一定收到&#xff1b;而是尽最大可能让数据送达目的主机&#xff0c;即使丢包也可以知道丢包。 目录 一. 确认应答和捎带应答机制 二. 超时重…