Commit ce422dbf by weijiguang

init

parents
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
/.idea/
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.ihaoin.hooloo.device"
minSdkVersion 24
targetSdkVersion 30
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation project(path: ':library_recyclerview')
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.kyleduo.switchbutton:library:2.0.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation 'com.github.bumptech.glide:glide:4.12.0'
implementation 'com.squareup.okhttp3:okhttp:3.5.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
annotationProcessor 'org.projectlombok:lombok:1.18.24'
compileOnly 'org.projectlombok:lombok:1.18.24'
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ihaoin.hooloo.device"
android:versionCode="1"
android:versionName="1.0">
<application
android:name=".HLApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name=".view.LauncherActivity"
android:label="@string/app_name"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
package com.ihaoin.hooloo.device;
import android.app.Application;
public class HLApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onTerminate() {
super.onTerminate();
}
}
package com.ihaoin.hooloo.device.data;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
@Data
public class Category implements Serializable {
/** 分类id */
private Integer id;
/** 分类名称 */
private String name;
/** 商品信息 */
private List<Goods> goods;
}
package com.ihaoin.hooloo.device.data;
import java.util.List;
import java.util.Map;
import lombok.Data;
@Data
public class Datas {
private Integer countOfOrder = 9;
private String tips = "http://www.baidu.com";
private List<String> leftImages;
private Map<Integer, String> rightImages;
private List<Recommend> recommends;
private List<Category> categorys;
}
package com.ihaoin.hooloo.device.data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
import lombok.Data;
@Data
public class Goods implements Serializable {
/** 商品id */
private Integer goodId;
/** 名称 */
private String name;
/** 原价 */
private BigDecimal price;
/** 折扣价 */
private BigDecimal discount;
/** 介绍 */
private String desc;
/** 备注 */
private String remark;
/** 图片信息 */
private Pics pics;
/** 规格 */
private List<GoodsSpec> specs;
/** 标签 */
private List<String> tags;
/** SKU */
private List<Sku> skus;
}
package com.ihaoin.hooloo.device.data;
import java.math.BigDecimal;
import java.util.List;
import lombok.Data;
@Data
public class GoodsRule {
/** 选项id */
private Integer ruleId;
/** 名称 */
private String ruleName;
/** 价格 */
private BigDecimal price;
}
package com.ihaoin.hooloo.device.data;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
@Data
public class GoodsSpec implements Serializable {
/** 规格id */
private Integer specId;
/** 名称 */
private String specName;
/** 选项列表 */
private List<GoodsRule> rules;
}
package com.ihaoin.hooloo.device.data;
import java.util.List;
import java.util.Map;
import lombok.Data;
@Data
public class Pics {
/** 缩略图 */
private String thumbnail;
/** 介绍图 */
private List<String> introImages;
/** 详情图 */
private List<String> detailImages;
}
package com.ihaoin.hooloo.device.data;
import java.io.Serializable;
import lombok.Data;
@Data
public class Recommend implements Serializable {
/** 推荐标题 */
private String title;
/** 推荐介绍 */
private String desc;
/** 推荐背景图 */
private String background;
/** 商品信息 */
private Goods goods;
}
package com.ihaoin.hooloo.device.data;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
@Data
public class Sku implements Serializable {
/** sku id */
private Integer skuId;
/** 状态:0-售罄,1-可售 */
private Integer state;
/** SKU规格 */
private List<SkuRule> rules;
}
package com.ihaoin.hooloo.device.data;
import java.math.BigDecimal;
import lombok.Data;
@Data
public class SkuRule {
/** 规格id */
private Integer specId;
/** 规格名称 */
private String specName;
/** 选项id */
private Integer ruleId;
/** 选项名称 */
private String ruleName;
/** 价格 */
private BigDecimal price;
}
package com.ihaoin.hooloo.device.util;
public class Utils {
}
package com.ihaoin.hooloo.device.view;
import android.app.Activity;
import android.os.Bundle;
import android.view.WindowManager;
import com.ihaoin.hooloo.device.R;
import java.math.BigDecimal;
public class LauncherActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launcher);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".view.LauncherActivity">
<include
layout="@layout/view_trolley"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="invisible" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/layout_recommends"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="7"
android:orientation="vertical" />
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rec_spu"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/right_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/black"
android:singleLine="true"
android:textSize="14sp" />
</FrameLayout>
<View
android:layout_width="0.5dp"
android:layout_height="match_parent"
android:background="@android:color/black" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rec_category"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
</FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="10dp">
<ToggleButton
android:id="@+id/butn_checked"
android:layout_width="30dp"
android:layout_height="30dp" />
<ImageView
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/txt_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="冰茶咖啡"
android:textColor="@android:color/black"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="@+id/txt_options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="冰/无糖"
android:textColor="@android:color/darker_gray"
android:textSize="12sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/txt_original_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ms__arrow"
android:gravity="center_vertical"
android:text="19.9"
android:textColor="@android:color/black"
android:textSize="12sp"
android:textStyle="bold" />
<TextView
android:id="@+id/txt_discount_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:drawableLeft="@drawable/ms__arrow"
android:gravity="center_vertical"
android:text="29.9"
android:textColor="@android:color/darker_gray"
android:textSize="9sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/butn_subtract"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/ms__arrow" />
<TextView
android:id="@+id/txt_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:text="1"
android:textColor="@android:color/black"
android:textSize="12sp" />
<Button
android:id="@+id/butn_add"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/ms__menu_down" />
</LinearLayout>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:text="确认订单"
android:textColor="@android:color/black"
android:textSize="24sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/img_qrcode"
android:layout_width="300dp"
android:layout_height="300dp"
android:src="@mipmap/qrcode" />
<LinearLayout
android:id="@+id/layout_wait_scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/txt_scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="使用微信扫一扫,确认订单并付款"
android:textColor="@android:color/black"
android:textSize="20sp" />
<TextView
android:id="@+id/txt_times"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="60秒后失效"
android:textColor="@android:color/black"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_scan_succeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:visibility="gone">
<ImageView
android:id="@+id/img_succeed"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@mipmap/qrcode" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="扫码成功"
android:textColor="@android:color/black"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="在微信小程序上确认订单并付款"
android:textColor="@android:color/black"
android:textSize="20sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<ImageView
android:id="@+id/butn_close"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:layout_margin="20dp"
android:src="@drawable/ms__arrow" />
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:id="@+id/img_intro"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_weight="1"
android:adjustViewBounds="true"
android:src="@mipmap/wx20220427" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/txt_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="冰茶咖啡"
android:textColor="@android:color/black"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:id="@+id/layout_categorys"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<LinearLayout
android:id="@+id/layout_images"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</LinearLayout>
</LinearLayout>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@android:color/darker_gray"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:id="@+id/txt_original_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ms__arrow"
android:gravity="center_vertical"
android:text="19.9"
android:textColor="@android:color/black"
android:textSize="12sp"
android:textStyle="bold" />
<TextView
android:id="@+id/txt_discount_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:drawableLeft="@drawable/ms__arrow"
android:gravity="center_vertical"
android:text="29.9"
android:textColor="@android:color/darker_gray"
android:textSize="9sp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/butn_subtract"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/ms__arrow" />
<TextView
android:id="@+id/txt_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:text="1"
android:textColor="@android:color/black"
android:textSize="12sp" />
<Button
android:id="@+id/butn_add"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/ms__menu_down" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<Button
android:id="@+id/butn_buy"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_weight="1"
android:text="立即购买" />
<Button
android:id="@+id/butn_trolley"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:text="加入购物袋" />
</LinearLayout>
</LinearLayout>
<ImageView
android:id="@+id/butn_close"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ms__arrow"
android:layout_alignParentRight="true"
android:layout_margin="20dp"/>
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/layout_trolley"
android:layout_width="400dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@android:color/holo_red_dark"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_weight="1"
android:text="购物袋"
android:textColor="@android:color/black"
android:textSize="18sp"
android:textStyle="bold" />
<Button
android:id="@+id/butn_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@null"
android:drawableLeft="@drawable/ms__menu_down"
android:gravity="right|center_vertical"
android:text="清空购物车"
android:textColor="@android:color/darker_gray"
android:textSize="12sp" />
</LinearLayout>
<ImageView
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@android:color/darker_gray" />
<ListView
android:id="@+id/list_trolley"
android:layout_width="match_parent"
android:layout_height="300dp"
android:divider="@null" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_settle_bar"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:gravity="center_vertical"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="60dp"
android:layout_height="match_parent">
<ImageView
android:id="@+id/img_trolley"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:src="@color/colorPrimaryDark" />
<TextView
android:id="@+id/txt_count"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentRight="true"
android:background="@android:color/holo_red_dark"
android:gravity="center"
android:text="9"
android:textSize="12sp" />
</RelativeLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/txt_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ms__menu_down"
android:text="10"
android:textColor="@android:color/black"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="@+id/txt_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="冰茶咖啡(冰)(全糖)x1 冰茶咖啡(冰)(无糖)x1"
android:textColor="@android:color/darker_gray"
android:textSize="12sp" />
</LinearLayout>
<Button
android:id="@+id/butn_pay"
android:layout_width="100dp"
android:layout_height="match_parent"
android:background="@android:color/black"
android:text="付款"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
</RelativeLayout>
\ No newline at end of file
<menu 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"
tools:context="com.ihaoin.hooloo.device.view.LauncherActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never" />
</menu>
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>
<resources>
<dimen name="fab_margin">16dp</dimen>
</resources>
<resources>
<string name="app_name">hooloo</string>
<string name="action_settings">Settings</string>
</resources>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.5.31'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
android.enableJetifier=true
org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
#android.buildCacheDir=./build/buildCache/
android.useAndroidX=true
android.enableD8=true
#android.enableBuildCache=true
#Sun Apr 24 15:23:27 CST 2022
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
.gradle/
.DS_Store
local.properties
# build files
build/
bin/
gen/
output/
# android studio
*.iml
.idea
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
//apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
minSdkVersion 14
targetSdkVersion 30
}
buildTypes {
release {
consumerProguardFiles 'proguard-rules.pro'
}
debug {
consumerProguardFiles 'proguard-rules.pro'
}
}
compileOptions {
kotlinOptions.freeCompilerArgs += "-module-name"
kotlinOptions.freeCompilerArgs += "com.github.CymChad.brvah"
}
lintOptions {
abortOnError false
}
}
// 打包源码jar
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
archiveClassifier = 'sources'
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.annotation:annotation:1.2.0'
implementation 'androidx.databinding:library:3.2.0-alpha11'
implementation 'androidx.databinding:viewbinding:4.2.2'
compileOnly 'androidx.recyclerview:recyclerview:1.2.1'
}
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/huasheng/Desktop/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
#-keep class com.chad.library.adapter.** {
#*;
#}
#-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
-keep public class * extends com.chad.library.adapter.base.viewholder.BaseViewHolder
-keepclassmembers class **$** extends com.chad.library.adapter.base.viewholder.BaseViewHolder {
<init>(...);
}
#-keepattributes InnerClasses
#
#-keep class androidx.** {*;}
#-keep public class * extends androidx.**
#-keep interface androidx.** {*;}
#-dontwarn androidx.**
\ No newline at end of file
<manifest package="com.chad.library"
xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
package com.chad.library.adapter.base
import android.annotation.SuppressLint
import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter.base.binder.BaseItemBinder
import com.chad.library.adapter.base.viewholder.BaseViewHolder
/**
* 使用 Binder 来实现adapter,既可以实现单布局,也能实现多布局
* 数据实体类也不存继承问题
*
* 当有多种条目的时候,避免在convert()中做太多的业务逻辑,把逻辑放在对应的 BaseItemBinder 中。
* 适用于以下情况:
* 1、实体类不方便扩展,此Adapter的数据类型可以是任意类型,默认情况下不需要实现 getItemType
* 2、item 类型较多,在convert()中管理起来复杂
*
* ViewHolder 由 [BaseItemBinder] 实现,并且每个[BaseItemBinder]可以拥有自己类型的ViewHolder类型。
*
* 数据类型为Any
*/
open class BaseBinderAdapter(list: MutableList<Any>? = null) : BaseQuickAdapter<Any, BaseViewHolder>(0, list) {
/**
* 用于存储每个 Binder 类型对应的 Diff
*/
private val classDiffMap = HashMap<Class<*>, DiffUtil.ItemCallback<Any>?>()
private val mTypeMap = HashMap<Class<*>, Int>()
private val mBinderArray = SparseArray<BaseItemBinder<Any, *>>()
init {
setDiffCallback(ItemCallback())
}
/**
* 添加 ItemBinder
*/
@JvmOverloads
fun <T : Any> addItemBinder(clazz: Class<out T>, baseItemBinder: BaseItemBinder<T, *>, callback: DiffUtil.ItemCallback<T>? = null): BaseBinderAdapter {
val itemType = mTypeMap.size + 1
mTypeMap[clazz] = itemType
mBinderArray.append(itemType, baseItemBinder as BaseItemBinder<Any, *>)
baseItemBinder._adapter = this
callback?.let {
classDiffMap[clazz] = it as DiffUtil.ItemCallback<Any>
}
return this
}
/**
* kotlin 可以使用如下方法添加 ItemBinder,更加简单
*/
inline fun <reified T : Any> addItemBinder(baseItemBinder: BaseItemBinder<T, *>, callback: DiffUtil.ItemCallback<T>? = null): BaseBinderAdapter {
addItemBinder(T::class.java, baseItemBinder, callback)
return this
}
override fun onCreateDefViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
return getItemBinder(viewType).let {
it._context = context
it.onCreateViewHolder(parent, viewType)
}
}
override fun convert(holder: BaseViewHolder, item: Any) {
getItemBinder(holder.itemViewType).convert(holder, item)
}
override fun convert(holder: BaseViewHolder, item: Any, payloads: List<Any>) {
getItemBinder(holder.itemViewType).convert(holder, item, payloads)
}
open fun getItemBinder(viewType: Int): BaseItemBinder<Any, BaseViewHolder> {
val binder = mBinderArray[viewType]
checkNotNull(binder) { "getItemBinder: viewType '$viewType' no such Binder found,please use addItemBinder() first!" }
return binder as BaseItemBinder<Any, BaseViewHolder>
}
open fun getItemBinderOrNull(viewType: Int): BaseItemBinder<Any, BaseViewHolder>? {
val binder = mBinderArray[viewType]
return binder as? BaseItemBinder<Any, BaseViewHolder>
}
override fun getDefItemViewType(position: Int): Int {
return findViewType(data[position].javaClass)
}
override fun bindViewClickListener(viewHolder: BaseViewHolder, viewType: Int) {
super.bindViewClickListener(viewHolder, viewType)
bindClick(viewHolder)
bindChildClick(viewHolder, viewType)
}
override fun onViewAttachedToWindow(holder: BaseViewHolder) {
super.onViewAttachedToWindow(holder)
getItemBinderOrNull(holder.itemViewType)?.onViewAttachedToWindow(holder)
}
override fun onViewDetachedFromWindow(holder: BaseViewHolder) {
super.onViewDetachedFromWindow(holder)
getItemBinderOrNull(holder.itemViewType)?.onViewDetachedFromWindow(holder)
}
override fun onFailedToRecycleView(holder: BaseViewHolder): Boolean {
return getItemBinderOrNull(holder.itemViewType)?.onFailedToRecycleView(holder) ?: false
}
protected fun findViewType(clazz : Class<*>):Int {
val type = mTypeMap[clazz]
checkNotNull(type) { "findViewType: ViewType: $clazz Not Find!" }
return type
}
protected open fun bindClick(viewHolder: BaseViewHolder) {
if (getOnItemClickListener() == null) {
//如果没有设置点击监听,则回调给 itemProvider
//Callback to itemProvider if no click listener is set
viewHolder.itemView.setOnClickListener {
var position = viewHolder.bindingAdapterPosition
if (position == RecyclerView.NO_POSITION) {
return@setOnClickListener
}
position -= headerLayoutCount
val itemViewType = viewHolder.itemViewType
val binder = getItemBinder(itemViewType)
binder.onClick(viewHolder, it, data[position], position)
}
}
if (getOnItemLongClickListener() == null) {
//如果没有设置长按监听,则回调给itemProvider
// If you do not set a long press listener, callback to the itemProvider
viewHolder.itemView.setOnLongClickListener {
var position = viewHolder.bindingAdapterPosition
if (position == RecyclerView.NO_POSITION) {
return@setOnLongClickListener false
}
position -= headerLayoutCount
val itemViewType = viewHolder.itemViewType
val binder = getItemBinder(itemViewType)
binder.onLongClick(viewHolder, it, data[position], position)
}
}
}
protected open fun bindChildClick(viewHolder: BaseViewHolder, viewType: Int) {
if (getOnItemChildClickListener() == null) {
val provider = getItemBinder(viewType)
val ids = provider.getChildClickViewIds()
ids.forEach { id ->
viewHolder.itemView.findViewById<View>(id)?.let {
if (!it.isClickable) {
it.isClickable = true
}
it.setOnClickListener { v ->
var position: Int = viewHolder.bindingAdapterPosition
if (position == RecyclerView.NO_POSITION) {
return@setOnClickListener
}
position -= headerLayoutCount
provider.onChildClick(viewHolder, v, data[position], position)
}
}
}
}
if (getOnItemChildLongClickListener() == null) {
val provider = getItemBinder(viewType)
val ids = provider.getChildLongClickViewIds()
ids.forEach { id ->
viewHolder.itemView.findViewById<View>(id)?.let {
if (!it.isLongClickable) {
it.isLongClickable = true
}
it.setOnLongClickListener { v ->
var position: Int = viewHolder.bindingAdapterPosition
if (position == RecyclerView.NO_POSITION) {
return@setOnLongClickListener false
}
position -= headerLayoutCount
provider.onChildLongClick(viewHolder, v, data[position], position)
}
}
}
}
}
/**
* Diff Callback
*/
private inner class ItemCallback : DiffUtil.ItemCallback<Any>() {
override fun areItemsTheSame(oldItem: Any, newItem: Any): Boolean {
if (oldItem.javaClass == newItem.javaClass) {
classDiffMap[oldItem.javaClass]?.let {
return it.areItemsTheSame(oldItem, newItem)
}
}
return oldItem == newItem
}
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(oldItem: Any, newItem: Any): Boolean {
if (oldItem.javaClass == newItem.javaClass) {
classDiffMap[oldItem.javaClass]?.let {
return it.areContentsTheSame(oldItem, newItem)
}
}
return true
}
override fun getChangePayload(oldItem: Any, newItem: Any): Any? {
if (oldItem.javaClass == newItem.javaClass) {
return classDiffMap[oldItem.javaClass]?.getChangePayload(oldItem, newItem)
}
return null
}
}
}
\ No newline at end of file
package com.chad.library.adapter.base
import android.view.ViewGroup
import com.chad.library.adapter.base.delegate.BaseMultiTypeDelegate
import com.chad.library.adapter.base.viewholder.BaseViewHolder
/**
* 多类型布局,通过代理类的方式,返回布局 id 和 item 类型;
* 适用于:
* 1、实体类不方便扩展,此Adapter的数据类型可以是任意类型,只需要在[BaseMultiTypeDelegate.getItemType]中返回对应类型
* 2、item 类型较少
* 如果类型较多,为了方便隔离各类型的业务逻辑,推荐使用[BaseBinderAdapter]
*
* @param T
* @param VH : BaseViewHolder
* @property mMultiTypeDelegate BaseMultiTypeDelegate<T>?
* @constructor
*/
abstract class BaseDelegateMultiAdapter<T, VH : BaseViewHolder>(data: MutableList<T>? = null) :
BaseQuickAdapter<T, VH>(0, data) {
private var mMultiTypeDelegate: BaseMultiTypeDelegate<T>? = null
/**
* 通过此方法设置代理
* @param multiTypeDelegate BaseMultiTypeDelegate<T>
*/
fun setMultiTypeDelegate(multiTypeDelegate: BaseMultiTypeDelegate<T>) {
this.mMultiTypeDelegate = multiTypeDelegate
}
fun getMultiTypeDelegate(): BaseMultiTypeDelegate<T>? = mMultiTypeDelegate
override fun onCreateDefViewHolder(parent: ViewGroup, viewType: Int): VH {
val delegate = getMultiTypeDelegate()
checkNotNull(delegate) { "Please use setMultiTypeDelegate first!" }
val layoutId = delegate.getLayoutId(viewType)
return createBaseViewHolder(parent, layoutId)
}
override fun getDefItemViewType(position: Int): Int {
val delegate = getMultiTypeDelegate()
checkNotNull(delegate) { "Please use setMultiTypeDelegate first!" }
return delegate.getItemType(data, position)
}
}
\ No newline at end of file
package com.chad.library.adapter.base
import android.util.SparseIntArray
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import com.chad.library.adapter.base.entity.MultiItemEntity
import com.chad.library.adapter.base.viewholder.BaseViewHolder
/**
* 多类型布局,适用于类型较少,业务不复杂的场景,便于快速使用。
* data[T]必须实现[MultiItemEntity]
*
* 如果数据类无法实现[MultiItemEntity],请使用[BaseDelegateMultiAdapter]
* 如果类型较多,为了方便隔离各类型的业务逻辑,推荐使用[BaseProviderMultiAdapter]
*
* @param T : MultiItemEntity
* @param VH : BaseViewHolder
* @constructor
*/
abstract class BaseMultiItemQuickAdapter<T : MultiItemEntity, VH : BaseViewHolder>(data: MutableList<T>? = null)
: BaseQuickAdapter<T, VH>(0, data) {
private val layouts: SparseIntArray by lazy(LazyThreadSafetyMode.NONE) { SparseIntArray() }
override fun getDefItemViewType(position: Int): Int {
return data[position].itemType
}
override fun onCreateDefViewHolder(parent: ViewGroup, viewType: Int): VH {
val layoutResId = layouts.get(viewType)
require(layoutResId != 0) { "ViewType: $viewType found layoutResId,please use addItemType() first!" }
return createBaseViewHolder(parent, layoutResId)
}
/**
* 调用此方法,设置多布局
* @param type Int
* @param layoutResId Int
*/
protected fun addItemType(type: Int, @LayoutRes layoutResId: Int) {
layouts.put(type, layoutResId)
}
}
\ No newline at end of file
package com.chad.library.adapter.base
import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter.base.provider.BaseItemProvider
import com.chad.library.adapter.base.viewholder.BaseViewHolder
/**
* 当有多种条目的时候,避免在convert()中做太多的业务逻辑,把逻辑放在对应的 ItemProvider 中。
* 适用于以下情况:
* 1、实体类不方便扩展,此Adapter的数据类型可以是任意类型,只需要在[getItemType]中返回对应类型
* 2、item 类型较多,在convert()中管理起来复杂
*
* ViewHolder 由 [BaseItemProvider] 实现,并且每个[BaseItemProvider]可以拥有自己类型的ViewHolder类型。
*
* @param T data 数据类型
* @constructor
*/
abstract class BaseProviderMultiAdapter<T>(data: MutableList<T>? = null) :
BaseQuickAdapter<T, BaseViewHolder>(0, data) {
private val mItemProviders by lazy(LazyThreadSafetyMode.NONE) { SparseArray<BaseItemProvider<T>>() }
/**
* 返回 item 类型
* @param data List<T>
* @param position Int
* @return Int
*/
protected abstract fun getItemType(data: List<T>, position: Int): Int
/**
* 必须通过此方法,添加 provider
* @param provider BaseItemProvider
*/
open fun addItemProvider(provider: BaseItemProvider<T>) {
provider.setAdapter(this)
mItemProviders.put(provider.itemViewType, provider)
}
override fun onCreateDefViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
val provider = getItemProvider(viewType)
checkNotNull(provider) { "ViewType: $viewType no such provider found,please use addItemProvider() first!" }
provider.context = parent.context
return provider.onCreateViewHolder(parent, viewType).apply {
provider.onViewHolderCreated(this, viewType)
}
}
override fun getDefItemViewType(position: Int): Int {
return getItemType(data, position)
}
override fun convert(holder: BaseViewHolder, item: T) {
getItemProvider(holder.itemViewType)!!.convert(holder, item)
}
override fun convert(holder: BaseViewHolder, item: T, payloads: List<Any>) {
getItemProvider(holder.itemViewType)!!.convert(holder, item, payloads)
}
override fun bindViewClickListener(viewHolder: BaseViewHolder, viewType: Int) {
super.bindViewClickListener(viewHolder, viewType)
bindClick(viewHolder)
bindChildClick(viewHolder, viewType)
}
/**
* 通过 ViewType 获取 BaseItemProvider
* 例如:如果ViewType经过特殊处理,可以重写此方法,获取正确的Provider
* (比如 ViewType 通过位运算进行的组合的)
*
* @param viewType Int
* @return BaseItemProvider
*/
protected open fun getItemProvider(viewType: Int): BaseItemProvider<T>? {
return mItemProviders.get(viewType)
}
override fun onViewAttachedToWindow(holder: BaseViewHolder) {
super.onViewAttachedToWindow(holder)
getItemProvider(holder.itemViewType)?.onViewAttachedToWindow(holder)
}
override fun onViewDetachedFromWindow(holder: BaseViewHolder) {
super.onViewDetachedFromWindow(holder)
getItemProvider(holder.itemViewType)?.onViewDetachedFromWindow(holder)
}
protected open fun bindClick(viewHolder: BaseViewHolder) {
if (getOnItemClickListener() == null) {
//如果没有设置点击监听,则回调给 itemProvider
//Callback to itemProvider if no click listener is set
viewHolder.itemView.setOnClickListener {
var position = viewHolder.bindingAdapterPosition
if (position == RecyclerView.NO_POSITION) {
return@setOnClickListener
}
position -= headerLayoutCount
val itemViewType = viewHolder.itemViewType
val provider = mItemProviders.get(itemViewType)
provider.onClick(viewHolder, it, data[position], position)
}
}
if (getOnItemLongClickListener() == null) {
//如果没有设置长按监听,则回调给itemProvider
// If you do not set a long press listener, callback to the itemProvider
viewHolder.itemView.setOnLongClickListener {
var position = viewHolder.bindingAdapterPosition
if (position == RecyclerView.NO_POSITION) {
return@setOnLongClickListener false
}
position -= headerLayoutCount
val itemViewType = viewHolder.itemViewType
val provider = mItemProviders.get(itemViewType)
provider.onLongClick(viewHolder, it, data[position], position)
}
}
}
protected open fun bindChildClick(viewHolder: BaseViewHolder, viewType: Int) {
if (getOnItemChildClickListener() == null) {
val provider = getItemProvider(viewType) ?: return
val ids = provider.getChildClickViewIds()
ids.forEach { id ->
viewHolder.itemView.findViewById<View>(id)?.let {
if (!it.isClickable) {
it.isClickable = true
}
it.setOnClickListener { v ->
var position: Int = viewHolder.bindingAdapterPosition
if (position == RecyclerView.NO_POSITION) {
return@setOnClickListener
}
position -= headerLayoutCount
provider.onChildClick(viewHolder, v, data[position], position)
}
}
}
}
if (getOnItemChildLongClickListener() == null) {
val provider = getItemProvider(viewType) ?: return
val ids = provider.getChildLongClickViewIds()
ids.forEach { id ->
viewHolder.itemView.findViewById<View>(id)?.let {
if (!it.isLongClickable) {
it.isLongClickable = true
}
it.setOnLongClickListener { v ->
var position: Int = viewHolder.bindingAdapterPosition
if (position == RecyclerView.NO_POSITION) {
return@setOnLongClickListener false
}
position -= headerLayoutCount
provider.onChildLongClick(viewHolder, v, data[position], position)
}
}
}
}
}
}
\ No newline at end of file
package com.chad.library.adapter.base
import androidx.annotation.LayoutRes
import com.chad.library.adapter.base.entity.SectionEntity
import com.chad.library.adapter.base.viewholder.BaseViewHolder
/**
* 快速实现带头部的 Adapter,由于本质属于多布局,所以继承自[BaseMultiItemQuickAdapter]
* @param T : SectionEntity
* @param VH : BaseViewHolder
* @property sectionHeadResId Int
* @constructor
*/
abstract class BaseSectionQuickAdapter<T : SectionEntity, VH : BaseViewHolder>
@JvmOverloads constructor(@LayoutRes private val sectionHeadResId: Int,
data: MutableList<T>? = null)
: BaseMultiItemQuickAdapter<T, VH>(data) {
constructor(@LayoutRes sectionHeadResId: Int,
@LayoutRes layoutResId: Int,
data: MutableList<T>? = null) : this(sectionHeadResId, data) {
setNormalLayout(layoutResId)
}
init {
addItemType(SectionEntity.HEADER_TYPE, sectionHeadResId)
}
/**
* 重写此处,设置 Header
* @param helper ViewHolder
* @param item data
*/
protected abstract fun convertHeader(helper: VH, item: T)
/**
* 重写此处,设置 Diff Header
* @param helper VH
* @param item T?
* @param payloads MutableList<Any>
*/
protected open fun convertHeader(helper: VH, item: T, payloads: MutableList<Any>) {}
/**
* 如果 item 不是多布局,可以使用此方法快速设置 item layout
* 如果需要多布局 item,请使用[addItemType]
* @param layoutResId Int
*/
protected fun setNormalLayout(@LayoutRes layoutResId: Int) {
addItemType(SectionEntity.NORMAL_TYPE, layoutResId)
}
override fun isFixedViewType(type: Int): Boolean {
return super.isFixedViewType(type) || type == SectionEntity.HEADER_TYPE
}
override fun onBindViewHolder(holder: VH, position: Int) {
if (holder.itemViewType == SectionEntity.HEADER_TYPE) {
// setFullSpan(holder)
convertHeader(holder, getItem(position - headerLayoutCount))
} else {
super.onBindViewHolder(holder, position)
}
}
override fun onBindViewHolder(holder: VH, position: Int, payloads: MutableList<Any>) {
if (payloads.isEmpty()) {
onBindViewHolder(holder, position)
return
}
if (holder.itemViewType == SectionEntity.HEADER_TYPE) {
convertHeader(holder, getItem(position - headerLayoutCount), payloads)
} else {
super.onBindViewHolder(holder, position, payloads)
}
}
}
\ No newline at end of file
package com.chad.library.adapter.base.animation
import android.animation.Animator
import android.animation.ObjectAnimator
import android.view.View
import android.view.animation.LinearInterpolator
/**
* https://github.com/CymChad/BaseRecyclerViewAdapterHelper
*/
class AlphaInAnimation @JvmOverloads constructor(private val mFrom: Float = DEFAULT_ALPHA_FROM) : BaseAnimation {
override fun animators(view: View): Array<Animator> {
val animator = ObjectAnimator.ofFloat(view, "alpha", mFrom, 1f)
animator.duration = 300L
animator.interpolator = LinearInterpolator()
return arrayOf(animator)
}
companion object {
private const val DEFAULT_ALPHA_FROM = 0f
}
}
\ No newline at end of file
package com.chad.library.adapter.base.animation
import android.animation.Animator
import android.view.View
/**
* https://github.com/CymChad/BaseRecyclerViewAdapterHelper
*/
interface BaseAnimation {
fun animators(view: View): Array<Animator>
}
\ No newline at end of file
package com.chad.library.adapter.base.animation
import android.animation.Animator
import android.animation.ObjectAnimator
import android.view.View
import android.view.animation.DecelerateInterpolator
/**
* https://github.com/CymChad/BaseRecyclerViewAdapterHelper
*/
class ScaleInAnimation @JvmOverloads constructor(private val mFrom: Float = DEFAULT_SCALE_FROM) : BaseAnimation {
override fun animators(view: View): Array<Animator> {
val scaleX = ObjectAnimator.ofFloat(view, "scaleX", mFrom, 1f)
scaleX.duration = 300L
scaleX.interpolator = DecelerateInterpolator()
val scaleY = ObjectAnimator.ofFloat(view, "scaleY", mFrom, 1f)
scaleY.duration = 300L
scaleY.interpolator = DecelerateInterpolator()
return arrayOf(scaleX, scaleY)
}
companion object {
private const val DEFAULT_SCALE_FROM = .5f
}
}
\ No newline at end of file
package com.chad.library.adapter.base.animation
import android.animation.Animator
import android.animation.ObjectAnimator
import android.view.View
import android.view.animation.DecelerateInterpolator
/**
* https://github.com/CymChad/BaseRecyclerViewAdapterHelper
*/
class SlideInBottomAnimation : BaseAnimation {
override fun animators(view: View): Array<Animator> {
val animator = ObjectAnimator.ofFloat(view, "translationY", view.measuredHeight.toFloat(), 0f)
animator.duration = 400L
animator.interpolator = DecelerateInterpolator(1.3f)
return arrayOf(animator)
}
}
\ No newline at end of file
package com.chad.library.adapter.base.animation
import android.animation.Animator
import android.animation.ObjectAnimator
import android.view.View
import android.view.animation.DecelerateInterpolator
/**
* https://github.com/CymChad/BaseRecyclerViewAdapterHelper
*/
class SlideInLeftAnimation : BaseAnimation {
override fun animators(view: View): Array<Animator> {
val animator = ObjectAnimator.ofFloat(view, "translationX", -view.rootView.width.toFloat(), 0f)
animator.duration = 400L
animator.interpolator = DecelerateInterpolator(1.8f)
return arrayOf(animator)
}
}
\ No newline at end of file
package com.chad.library.adapter.base.animation
import android.animation.Animator
import android.animation.ObjectAnimator
import android.view.View
import android.view.animation.DecelerateInterpolator
/**
* https://github.com/CymChad/BaseRecyclerViewAdapterHelper
*/
class SlideInRightAnimation : BaseAnimation {
override fun animators(view: View): Array<Animator> {
val animator = ObjectAnimator.ofFloat(view, "translationX", view.rootView.width.toFloat(), 0f)
animator.duration = 400L
animator.interpolator = DecelerateInterpolator(1.8f)
return arrayOf(animator)
}
}
\ No newline at end of file
package com.chad.library.adapter.base.binder
import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.annotation.IdRes
import com.chad.library.adapter.base.BaseBinderAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
/**
* Binder 的基类
*/
abstract class BaseItemBinder<T, VH : BaseViewHolder> {
private val clickViewIds by lazy(LazyThreadSafetyMode.NONE) { ArrayList<Int>() }
private val longClickViewIds by lazy(LazyThreadSafetyMode.NONE) { ArrayList<Int>() }
internal var _adapter: BaseBinderAdapter? = null
internal var _context: Context? = null
val adapter: BaseBinderAdapter
get() {
checkNotNull(_adapter) {
"""This $this has not been attached to BaseBinderAdapter yet.
You should not call the method before addItemBinder()."""
}
return _adapter!!
}
val context: Context
get() {
checkNotNull(_context) {
"""This $this has not been attached to BaseBinderAdapter yet.
You should not call the method before onCreateViewHolder()."""
}
return _context!!
}
val data: MutableList<Any> get() = adapter.data
abstract fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH
/**
* 在此处对设置item数据
* @param holder VH
* @param data T
*/
abstract fun convert(holder: VH, data: T)
/**
* 使用局部刷新时候,会调用此方法
* @param holder VH
* @param data T
* @param payloads List<Any>
*/
open fun convert(holder: VH, data: T, payloads: List<Any>) {}
open fun onFailedToRecycleView(holder: VH): Boolean {
return false
}
/**
* Called when a view created by this [BaseItemBinder] has been attached to a window.
* 当此[BaseItemBinder]出现在屏幕上的时候,会调用此方法
*
* This can be used as a reasonable signal that the view is about to be seen
* by the user. If the [BaseItemBinder] previously freed any resources in
* [onViewDetachedFromWindow][.onViewDetachedFromWindow]
* those resources should be restored here.
*
* @param holder Holder of the view being attached
*/
open fun onViewAttachedToWindow(holder: VH) {}
/**
* Called when a view created by this [BaseItemBinder] has been detached from its
* window.
* 当此[BaseItemBinder]从屏幕上移除的时候,会调用此方法
*
* Becoming detached from the window is not necessarily a permanent condition;
* the consumer of an Adapter's views may choose to cache views offscreen while they
* are not visible, attaching and detaching them as appropriate.
*
* @param holder Holder of the view being detached
*/
open fun onViewDetachedFromWindow(holder: VH) {}
/**
* item 若想实现条目点击事件则重写该方法
* @param holder VH
* @param data T
* @param position Int
*/
open fun onClick(holder: VH, view: View, data: T, position: Int) {}
/**
* item 若想实现条目长按事件则重写该方法
* @param holder VH
* @param data T
* @param position Int
* @return Boolean
*/
open fun onLongClick(holder: VH, view: View, data: T, position: Int): Boolean {
return false
}
/**
* item 子控件的点击事件
* @param holder VH
* @param view View
* @param data T
* @param position Int
*/
open fun onChildClick(holder: VH, view: View, data: T, position: Int) {}
/**
* item 子控件的长按事件
* @param holder VH
* @param view View
* @param data T
* @param position Int
* @return Boolean
*/
open fun onChildLongClick(holder: VH, view: View, data: T, position: Int): Boolean {
return false
}
fun addChildClickViewIds(@IdRes vararg ids: Int) {
ids.forEach {
this.clickViewIds.add(it)
}
}
fun getChildClickViewIds() = this.clickViewIds
fun addChildLongClickViewIds(@IdRes vararg ids: Int) {
ids.forEach {
this.longClickViewIds.add(it)
}
}
fun getChildLongClickViewIds() = this.longClickViewIds
}
\ No newline at end of file
package com.chad.library.adapter.base.binder
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.ViewDataBinding
import com.chad.library.adapter.base.viewholder.BaseViewHolder
/**
* 使用 DataBinding 快速构建 Binder
* @param T item数据类型
* @param DB : ViewDataBinding
*/
abstract class QuickDataBindingItemBinder<T, DB : ViewDataBinding> : BaseItemBinder<T, QuickDataBindingItemBinder.BinderDataBindingHolder<DB>>() {
/**
* 此 Holder 不适用于其他 BaseAdapter,仅针对[BaseBinderAdapter]
*/
class BinderDataBindingHolder<DB : ViewDataBinding>(val dataBinding: DB) : BaseViewHolder(dataBinding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BinderDataBindingHolder<DB> {
return BinderDataBindingHolder(onCreateDataBinding(LayoutInflater.from(parent.context), parent, viewType))
}
abstract fun onCreateDataBinding(layoutInflater: LayoutInflater, parent: ViewGroup, viewType: Int): DB
}
\ No newline at end of file
package com.chad.library.adapter.base.binder
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import com.chad.library.adapter.base.util.getItemView
import com.chad.library.adapter.base.viewholder.BaseViewHolder
/**
* 使用布局 ID 快速构建 Binder
* @param T item 数据类型
*/
abstract class QuickItemBinder<T> : BaseItemBinder<T, BaseViewHolder>() {
@LayoutRes
abstract fun getLayoutId(): Int
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder =
BaseViewHolder(parent.getItemView(getLayoutId()))
}
package com.chad.library.adapter.base.binder
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.viewbinding.ViewBinding
import com.chad.library.adapter.base.viewholder.BaseViewHolder
/**
* 使用 ViewBinding 快速构建 Binder
* @param T item数据类型
* @param VB : ViewBinding
*/
abstract class QuickViewBindingItemBinder<T, VB : ViewBinding> : BaseItemBinder<T, QuickViewBindingItemBinder.BinderVBHolder<VB>>() {
/**
* 此 Holder 不适用于其他 BaseAdapter,仅针对[BaseBinderAdapter]
*/
class BinderVBHolder<VB : ViewBinding>(val viewBinding: VB) : BaseViewHolder(viewBinding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BinderVBHolder<VB> {
return BinderVBHolder(onCreateViewBinding(LayoutInflater.from(parent.context), parent, viewType))
}
abstract fun onCreateViewBinding(layoutInflater: LayoutInflater, parent: ViewGroup, viewType: Int): VB
}
\ No newline at end of file
package com.chad.library.adapter.base.delegate
import android.util.SparseIntArray
import androidx.annotation.LayoutRes
/**
* help you to achieve multi type easily
*
*
* Created by tysheng
* Date: 2017/4/6 08:41.
* Email: tyshengsx@gmail.com
*
* more information: https://github.com/CymChad/BaseRecyclerViewAdapterHelper/issues/968
*/
abstract class BaseMultiTypeDelegate<T>(private var layouts: SparseIntArray = SparseIntArray()) {
private var autoMode: Boolean = false
private var selfMode: Boolean = false
/**
* get the item type from specific entity.
*
* @param data entity
* @param position
* @return item type
*/
abstract fun getItemType(data: List<T>, position: Int): Int
fun getLayoutId(viewType: Int): Int {
val layoutResId = layouts.get(viewType)
require(layoutResId != 0) { "ViewType: $viewType found layoutResId,please use registerItemType() first!" }
return layoutResId
}
private fun registerItemType(type: Int, @LayoutRes layoutResId: Int) {
this.layouts.put(type, layoutResId)
}
/**
* auto increase type vale, start from 0.
*
* @param layoutResIds layout id arrays
* @return MultiTypeDelegate
*/
fun addItemTypeAutoIncrease(@LayoutRes vararg layoutResIds: Int): BaseMultiTypeDelegate<T> {
autoMode = true
checkMode(selfMode)
for (i in layoutResIds.indices) {
registerItemType(i, layoutResIds[i])
}
return this
}
/**
* set your own type one by one.
*
* @param type type value
* @param layoutResId layout id
* @return MultiTypeDelegate
*/
fun addItemType(type: Int, @LayoutRes layoutResId: Int): BaseMultiTypeDelegate<T> {
selfMode = true
checkMode(autoMode)
registerItemType(type, layoutResId)
return this
}
private fun checkMode(mode: Boolean) {
require(!mode) { "Don't mess two register mode" }
}
}
package com.chad.library.adapter.base.diff
import android.os.Handler
import android.os.Looper
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.DiffUtil.DiffResult
import androidx.recyclerview.widget.ListUpdateCallback
import com.chad.library.adapter.base.BaseQuickAdapter
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.Executor
class BrvahAsyncDiffer<T>(private val adapter: BaseQuickAdapter<T, *>,
private val config: BrvahAsyncDifferConfig<T>) : DifferImp<T> {
private val mUpdateCallback: ListUpdateCallback = BrvahListUpdateCallback(adapter)
private var mMainThreadExecutor: Executor
private class MainThreadExecutor internal constructor() : Executor {
val mHandler = Handler(Looper.getMainLooper())
override fun execute(command: Runnable) {
mHandler.post(command)
}
}
private val sMainThreadExecutor: Executor = MainThreadExecutor()
init {
mMainThreadExecutor = config.mainThreadExecutor ?: sMainThreadExecutor
}
private val mListeners: MutableList<ListChangeListener<T>> = CopyOnWriteArrayList()
private var mMaxScheduledGeneration = 0
fun addData(index: Int, data: T) {
val previousList: List<T> = adapter.data
adapter.data.add(index, data)
// final List<T> previousList = mReadOnlyList;
// mReadOnlyList = Collections.unmodifiableList(adapterData);
mUpdateCallback.onInserted(index, 1)
onCurrentListChanged(previousList, null)
}
fun addData(data: T) {
val previousList: List<T> = adapter.data
adapter.data.add(data)
mUpdateCallback.onInserted(previousList.size, 1)
onCurrentListChanged(previousList, null)
}
fun addList(list: List<T>?) {
if (list == null) return
val previousList: List<T> = adapter.data
adapter.data.addAll(list)
// final List<T> previousList = mReadOnlyList;
// mReadOnlyList = Collections.unmodifiableList(adapterData);
mUpdateCallback.onInserted(previousList.size, list.size)
onCurrentListChanged(previousList, null)
}
/**
* 改变某一个数据
*/
fun changeData(index: Int, newData: T, payload: T?) {
val previousList: List<T> = adapter.data
adapter.data[index] = newData
// final List<T> previousList = mReadOnlyList;
// mReadOnlyList = Collections.unmodifiableList(adapterData);
mUpdateCallback.onChanged(index, 1, payload)
onCurrentListChanged(previousList, null)
}
/**
* 移除某一个数据
*/
fun removeAt(index: Int) {
val previousList: List<T> = adapter.data
adapter.data.removeAt(index)
// final List<T> previousList = mReadOnlyList;
// mReadOnlyList = Collections.unmodifiableList(adapterData);
mUpdateCallback.onRemoved(index, 1)
onCurrentListChanged(previousList, null)
}
fun remove(t: T) {
val previousList: List<T> = adapter.data
val index = adapter.data.indexOf(t)
if (index == -1) return
adapter.data.removeAt(index)
// final List<T> previousList = mReadOnlyList;
// mReadOnlyList = Collections.unmodifiableList(adapterData);
mUpdateCallback.onRemoved(index, 1)
onCurrentListChanged(previousList, null)
}
@JvmOverloads
fun submitList(newList: MutableList<T>?, commitCallback: Runnable? = null) {
// incrementing generation means any currently-running diffs are discarded when they finish
val runGeneration: Int = ++mMaxScheduledGeneration
if (newList === adapter.data) {
// nothing to do (Note - still had to inc generation, since may have ongoing work)
commitCallback?.run()
return
}
val oldList: List<T> = adapter.data
// fast simple remove all
if (newList == null) {
val countRemoved: Int = adapter.data.size
adapter.data = arrayListOf()
// notify last, after list is updated
mUpdateCallback.onRemoved(0, countRemoved)
onCurrentListChanged(oldList, commitCallback)
return
}
// fast simple first insert
if (adapter.data.isEmpty()) {
adapter.data = newList
// notify last, after list is updated
mUpdateCallback.onInserted(0, newList.size)
onCurrentListChanged(oldList, commitCallback)
return
}
config.backgroundThreadExecutor.execute {
val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem: T? = oldList[oldItemPosition]
val newItem: T? = newList[newItemPosition]
return if (oldItem != null && newItem != null) {
config.diffCallback.areItemsTheSame(oldItem, newItem)
} else oldItem == null && newItem == null
// If both items are null we consider them the same.
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem: T? = oldList[oldItemPosition]
val newItem: T? = newList[newItemPosition]
if (oldItem != null && newItem != null) {
return config.diffCallback.areContentsTheSame(oldItem, newItem)
}
if (oldItem == null && newItem == null) {
return true
}
throw AssertionError()
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem: T? = oldList[oldItemPosition]
val newItem: T? = newList[newItemPosition]
if (oldItem != null && newItem != null) {
return config.diffCallback.getChangePayload(oldItem, newItem)
}
throw AssertionError()
}
})
mMainThreadExecutor.execute {
if (mMaxScheduledGeneration == runGeneration) {
latchList(newList, result, commitCallback)
}
}
}
}
private fun latchList(
newList: MutableList<T>,
diffResult: DiffResult,
commitCallback: Runnable?) {
val previousList: List<T> = adapter.data
adapter.data = newList
diffResult.dispatchUpdatesTo(mUpdateCallback)
onCurrentListChanged(previousList, commitCallback)
}
private fun onCurrentListChanged(previousList: List<T>,
commitCallback: Runnable?) {
for (listener in mListeners) {
listener.onCurrentListChanged(previousList, adapter.data)
}
commitCallback?.run()
}
/**
* Add a ListListener to receive updates when the current List changes.
*
* @param listener Listener to receive updates.
*
* @see .getCurrentList
* @see .removeListListener
*/
override fun addListListener(listener: ListChangeListener<T>) {
mListeners.add(listener)
}
/**
* Remove a previously registered ListListener.
*
* @param listener Previously registered listener.
* @see .getCurrentList
* @see .addListListener
*/
fun removeListListener(listener: ListChangeListener<T>) {
mListeners.remove(listener)
}
fun clearAllListListener() {
mListeners.clear()
}
}
\ No newline at end of file
package com.chad.library.adapter.base.diff
import androidx.annotation.RestrictTo
import androidx.recyclerview.widget.DiffUtil
import java.util.concurrent.Executor
import java.util.concurrent.Executors
class BrvahAsyncDifferConfig<T>(
@RestrictTo(RestrictTo.Scope.LIBRARY)
val mainThreadExecutor: Executor?,
val backgroundThreadExecutor: Executor,
val diffCallback: DiffUtil.ItemCallback<T>) {
/**
* Builder class for [BrvahAsyncDifferConfig].
*
* @param <T>
</T> */
class Builder<T>(private val mDiffCallback: DiffUtil.ItemCallback<T>) {
private var mMainThreadExecutor: Executor? = null
private var mBackgroundThreadExecutor: Executor? = null
/**
* If provided, defines the main thread executor used to dispatch adapter update
* notifications on the main thread.
*
*
* If not provided, it will default to the main thread.
*
* @param executor The executor which can run tasks in the UI thread.
* @return this
*
* @hide
*/
fun setMainThreadExecutor(executor: Executor?): Builder<T> {
mMainThreadExecutor = executor
return this
}
/**
* If provided, defines the background executor used to calculate the diff between an old
* and a new list.
*
*
* If not provided, defaults to two thread pool executor, shared by all ListAdapterConfigs.
*
* @param executor The background executor to run list diffing.
* @return this
*/
fun setBackgroundThreadExecutor(executor: Executor?): Builder<T> {
mBackgroundThreadExecutor = executor
return this
}
/**
* Creates a [BrvahAsyncDifferConfig] with the given parameters.
*
* @return A new AsyncDifferConfig.
*/
fun build(): BrvahAsyncDifferConfig<T> {
if (mBackgroundThreadExecutor == null) {
synchronized(sExecutorLock) {
if (sDiffExecutor == null) {
sDiffExecutor = Executors.newFixedThreadPool(2)
}
}
mBackgroundThreadExecutor = sDiffExecutor
}
return BrvahAsyncDifferConfig(
mMainThreadExecutor,
mBackgroundThreadExecutor!!,
mDiffCallback)
}
companion object {
// TODO: remove the below once supportlib has its own appropriate executors
private val sExecutorLock = Any()
private var sDiffExecutor: Executor? = null
}
}
}
\ No newline at end of file
package com.chad.library.adapter.base.diff
import androidx.recyclerview.widget.ListUpdateCallback
import com.chad.library.adapter.base.BaseQuickAdapter
class BrvahListUpdateCallback(private val mAdapter: BaseQuickAdapter<*, *>) : ListUpdateCallback {
override fun onInserted(position: Int, count: Int) {
mAdapter.notifyItemRangeInserted(position + mAdapter.headerLayoutCount, count)
}
override fun onRemoved(position: Int, count: Int) {
if (mAdapter.mLoadMoreModule?.hasLoadMoreView() == true && mAdapter.itemCount == 0) {
// 如果注册了加载更多,并且当前itemCount为0,则需要加上loadMore所占用的一行
mAdapter.notifyItemRangeRemoved(position + mAdapter.headerLayoutCount, count + 1)
} else {
mAdapter.notifyItemRangeRemoved(position + mAdapter.headerLayoutCount, count)
}
}
override fun onMoved(fromPosition: Int, toPosition: Int) {
mAdapter.notifyItemMoved(fromPosition + mAdapter.headerLayoutCount, toPosition + mAdapter.headerLayoutCount)
}
override fun onChanged(position: Int, count: Int, payload: Any?) {
mAdapter.notifyItemRangeChanged(position + mAdapter.headerLayoutCount, count, payload)
}
}
\ No newline at end of file
package com.chad.library.adapter.base.diff;
import androidx.annotation.NonNull;
/**
* 使用java接口定义方法
* @param <T>
*/
public interface DifferImp<T> {
void addListListener(@NonNull ListChangeListener<T> listChangeListener);
}
package com.chad.library.adapter.base.diff;
import androidx.annotation.NonNull;
import java.util.List;
public interface ListChangeListener<T> {
/**
* Called after the current List has been updated.
*
* @param previousList The previous list.
* @param currentList The new current list.
*/
void onCurrentListChanged(@NonNull List<T> previousList, @NonNull List<T> currentList);
}
package com.chad.library.adapter.base.dragswipe;
import android.graphics.Canvas;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import com.chad.library.R;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.module.BaseDraggableModule;
/**
* @author luoxw
* @date 2016/6/20
*/
public class DragAndSwipeCallback extends ItemTouchHelper.Callback {
private BaseDraggableModule mDraggableModule;
private float mMoveThreshold = 0.1f;
private float mSwipeThreshold = 0.7f;
private int mDragMoveFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
private int mSwipeMoveFlags = ItemTouchHelper.END;
public DragAndSwipeCallback(BaseDraggableModule draggableModule) {
mDraggableModule = draggableModule;
}
@Override
public boolean isLongPressDragEnabled() {
if (mDraggableModule != null) {
return mDraggableModule.isDragEnabled() && !mDraggableModule.hasToggleView();
}
return false;
}
@Override
public boolean isItemViewSwipeEnabled() {
if (mDraggableModule != null) {
return mDraggableModule.isSwipeEnabled();
}
return false;
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG
&& !isViewCreateByAdapter(viewHolder)) {
if (mDraggableModule != null) {
mDraggableModule.onItemDragStart(viewHolder);
}
viewHolder.itemView.setTag(R.id.BaseQuickAdapter_dragging_support, true);
} else if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE
&& !isViewCreateByAdapter(viewHolder)) {
if (mDraggableModule != null) {
mDraggableModule.onItemSwipeStart(viewHolder);
}
viewHolder.itemView.setTag(R.id.BaseQuickAdapter_swiping_support, true);
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if (isViewCreateByAdapter(viewHolder)) {
return;
}
if (viewHolder.itemView.getTag(R.id.BaseQuickAdapter_dragging_support) != null
&& (Boolean) viewHolder.itemView.getTag(R.id.BaseQuickAdapter_dragging_support)) {
if (mDraggableModule != null) {
mDraggableModule.onItemDragEnd(viewHolder);
}
viewHolder.itemView.setTag(R.id.BaseQuickAdapter_dragging_support, false);
}
if (viewHolder.itemView.getTag(R.id.BaseQuickAdapter_swiping_support) != null
&& (Boolean) viewHolder.itemView.getTag(R.id.BaseQuickAdapter_swiping_support)) {
if (mDraggableModule != null) {
mDraggableModule.onItemSwipeClear(viewHolder);
}
viewHolder.itemView.setTag(R.id.BaseQuickAdapter_swiping_support, false);
}
}
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
if (isViewCreateByAdapter(viewHolder)) {
return makeMovementFlags(0, 0);
}
return makeMovementFlags(mDragMoveFlags, mSwipeMoveFlags);
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder source, @NonNull RecyclerView.ViewHolder target) {
return source.getItemViewType() == target.getItemViewType();
}
@Override
public void onMoved(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder source, int fromPos, @NonNull RecyclerView.ViewHolder target, int toPos, int x, int y) {
super.onMoved(recyclerView, source, fromPos, target, toPos, x, y);
if (mDraggableModule != null) {
mDraggableModule.onItemDragMoving(source, target);
}
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
if (!isViewCreateByAdapter(viewHolder)) {
if (mDraggableModule != null) {
mDraggableModule.onItemSwiped(viewHolder);
}
}
}
@Override
public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
return mMoveThreshold;
}
@Override
public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
return mSwipeThreshold;
}
/**
* Set the fraction that the user should move the View to be considered as swiped.
* The fraction is calculated with respect to RecyclerView's bounds.
* <p>
* Default value is .5f, which means, to swipe a View, user must move the View at least
* half of RecyclerView's width or height, depending on the swipe direction.
*
* @param swipeThreshold A float value that denotes the fraction of the View size. Default value
* is .8f .
*/
public void setSwipeThreshold(float swipeThreshold) {
mSwipeThreshold = swipeThreshold;
}
/**
* Set the fraction that the user should move the View to be considered as it is
* dragged. After a view is moved this amount, ItemTouchHelper starts checking for Views
* below it for a possible drop.
*
* @param moveThreshold A float value that denotes the fraction of the View size. Default value is
* .1f .
*/
public void setMoveThreshold(float moveThreshold) {
mMoveThreshold = moveThreshold;
}
/**
* <p>Set the drag movement direction.</p>
* <p>The value should be ItemTouchHelper.UP, ItemTouchHelper.DOWN, ItemTouchHelper.LEFT, ItemTouchHelper.RIGHT or their combination.</p>
* You can combine them like ItemTouchHelper.UP | ItemTouchHelper.DOWN, it means that the item could only move up and down when dragged.
*
* @param dragMoveFlags the drag movement direction. Default value is ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT.
*/
public void setDragMoveFlags(int dragMoveFlags) {
mDragMoveFlags = dragMoveFlags;
}
/**
* <p>Set the swipe movement direction.</p>
* <p>The value should be ItemTouchHelper.START, ItemTouchHelper.END or their combination.</p>
* You can combine them like ItemTouchHelper.START | ItemTouchHelper.END, it means that the item could swipe to both left or right.
*
* @param swipeMoveFlags the swipe movement direction. Default value is ItemTouchHelper.END.
*/
public void setSwipeMoveFlags(int swipeMoveFlags) {
mSwipeMoveFlags = swipeMoveFlags;
}
@Override
public void onChildDrawOver(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder,
float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE
&& !isViewCreateByAdapter(viewHolder)) {
View itemView = viewHolder.itemView;
c.save();
if (dX > 0) {
c.clipRect(itemView.getLeft(), itemView.getTop(),
itemView.getLeft() + dX, itemView.getBottom());
c.translate(itemView.getLeft(), itemView.getTop());
} else {
c.clipRect(itemView.getRight() + dX, itemView.getTop(),
itemView.getRight(), itemView.getBottom());
c.translate(itemView.getRight() + dX, itemView.getTop());
}
if (mDraggableModule != null) {
mDraggableModule.onItemSwiping(c, viewHolder, dX, dY, isCurrentlyActive);
}
c.restore();
}
}
private boolean isViewCreateByAdapter(@NonNull RecyclerView.ViewHolder viewHolder) {
int type = viewHolder.getItemViewType();
return type == BaseQuickAdapter.HEADER_VIEW || type == BaseQuickAdapter.LOAD_MORE_VIEW
|| type == BaseQuickAdapter.FOOTER_VIEW || type == BaseQuickAdapter.EMPTY_VIEW;
}
}
package com.chad.library.adapter.base.entity;
/**
* 仅供java使用
*
* 由于java无法实现{@link SectionEntity}中的默认接口实现,所以使用抽象类再封装一次,用于提供默认实现。
*/
public abstract class JSectionEntity implements SectionEntity {
/**
* 用于返回item类型,除了头布局外,默认只有 NORMAL_TYPE 一种布局
* 如果需要实现 item 多布局,请重写此方法,返回自己的type
*/
@Override
public int getItemType() {
if (isHeader()) {
return SectionEntity.Companion.HEADER_TYPE;
} else {
// 拷贝 重写此处,返回自己的多布局类型
return SectionEntity.Companion.NORMAL_TYPE;
}
}
}
package com.chad.library.adapter.base.entity
/**
* 多布局类型
*/
interface MultiItemEntity {
val itemType: Int
}
package com.chad.library.adapter.base.entity
/**
* 带头部布局的实体类接口
* 实体类请继承此接口;如果使用java,请使用[JSectionEntity]抽象类
*/
interface SectionEntity : MultiItemEntity {
val isHeader: Boolean
/**
* 用于返回item类型,除了头布局外,默认只有[NORMAL_TYPE]一种布局
* 如果需要实现 item 多布局,请重写此方法,返回自己的type
*/
override val itemType: Int
get() = if (isHeader) HEADER_TYPE else NORMAL_TYPE
companion object {
const val NORMAL_TYPE = -100
const val HEADER_TYPE = -99
}
}
package com.chad.library.adapter.base.entity.node
abstract class BaseExpandNode : BaseNode() {
var isExpanded: Boolean = true
}
\ No newline at end of file
package com.chad.library.adapter.base.entity.node
abstract class BaseNode {
/**
* 重写此方法,获取子节点。如果没有子节点,返回 null 或者 空数组
*
* 如果返回 null,则无法对子节点的数据进行新增和删除等操作
*/
abstract val childNode: MutableList<BaseNode>?
}
\ No newline at end of file
package com.chad.library.adapter.base.entity.node
/**
* 如果需要,可以实现此接口,返回脚部节点
*/
interface NodeFooterImp {
/**
* 返回脚部节点
* @return BaseNode? 如果返回 null,则代表没有脚部节点
*/
val footerNode: BaseNode?
}
\ No newline at end of file
package com.chad.library.adapter.base.listener;
import androidx.annotation.Nullable;
/**
* @author: limuyang
* @date: 2019-12-05
* @Description:
*/
public interface DraggableListenerImp {
void setOnItemDragListener(@Nullable OnItemDragListener onItemDragListener);
void setOnItemSwipeListener(@Nullable OnItemSwipeListener onItemSwipeListener);
}
package com.chad.library.adapter.base.listener;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
/**
* @author: limuyang
* @date: 2019-12-03
* @Description:
*/
public interface GridSpanSizeLookup {
int getSpanSize(@NonNull GridLayoutManager gridLayoutManager, int viewType, int position);
}
package com.chad.library.adapter.base.listener;
import androidx.annotation.Nullable;
/**
* @author: limuyang
* @date: 2019-12-03
* @Description: LoadMore需要设置的接口。使用java定义,以兼容java写法
*/
public interface LoadMoreListenerImp {
void setOnLoadMoreListener(@Nullable OnLoadMoreListener listener);
}
package com.chad.library.adapter.base.listener;
import android.view.View;
import androidx.annotation.NonNull;
import com.chad.library.adapter.base.BaseQuickAdapter;
/**
* @author: limuyang
* @date: 2019-12-03
* @Description:
*/
public interface OnItemChildClickListener {
/**
* callback method to be invoked when an item child in this view has been click
*
* @param adapter BaseQuickAdapter
* @param view The view whihin the ItemView that was clicked
* @param position The position of the view int the adapter
*/
void onItemChildClick(@NonNull BaseQuickAdapter<?,?> adapter, @NonNull View view, int position);
}
package com.chad.library.adapter.base.listener;
import android.view.View;
import androidx.annotation.NonNull;
import com.chad.library.adapter.base.BaseQuickAdapter;
/**
* @author: limuyang
* @date: 2019-12-03
* @Description:
*/
public interface OnItemChildLongClickListener {
/**
* callback method to be invoked when an item in this view has been
* click and held
*
* @param adapter this BaseQuickAdapter adapter
* @param view The childView whihin the itemView that was clicked and held.
* @param position The position of the view int the adapter
* @return true if the callback consumed the long click ,false otherwise
*/
boolean onItemChildLongClick(@NonNull BaseQuickAdapter<?,?> adapter, @NonNull View view, int position);
}
package com.chad.library.adapter.base.listener;
import android.view.View;
import androidx.annotation.NonNull;
import com.chad.library.adapter.base.BaseQuickAdapter;
/**
* @author: limuyang
* @date: 2019-12-03
* @Description: Interface definition for a callback to be invoked when an item in this
* RecyclerView itemView has been clicked.
*/
public interface OnItemClickListener {
/**
* Callback method to be invoked when an item in this RecyclerView has
* been clicked.
*
* @param adapter the adapter
* @param view The itemView within the RecyclerView that was clicked (this
* will be a view provided by the adapter)
* @param position The position of the view in the adapter.
*/
void onItemClick(@NonNull BaseQuickAdapter<?,?> adapter, @NonNull View view, int position);
}
package com.chad.library.adapter.base.listener;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by luoxw on 2016/6/20.
*/
public interface OnItemDragListener {
void onItemDragStart(RecyclerView.ViewHolder viewHolder, int pos);
void onItemDragMoving(RecyclerView.ViewHolder source, int from, RecyclerView.ViewHolder target, int to);
void onItemDragEnd(RecyclerView.ViewHolder viewHolder, int pos);
}
package com.chad.library.adapter.base.listener;
import android.view.View;
import androidx.annotation.NonNull;
import com.chad.library.adapter.base.BaseQuickAdapter;
/**
* @author: limuyang
* @date: 2019-12-03
* @Description:
*/
public interface OnItemLongClickListener {
/**
* callback method to be invoked when an item in this view has been
* click and held
*
* @param adapter the adapter
* @param view The view whihin the RecyclerView that was clicked and held.
* @param position The position of the view int the adapter
* @return true if the callback consumed the long click ,false otherwise
*/
boolean onItemLongClick(@NonNull BaseQuickAdapter<?,?> adapter, @NonNull View view, int position);
}
package com.chad.library.adapter.base.listener;
import android.graphics.Canvas;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by luoxw on 2016/6/23.
*/
public interface OnItemSwipeListener {
/**
* Called when the swipe action start.
*/
void onItemSwipeStart(RecyclerView.ViewHolder viewHolder, int pos);
/**
* Called when the swipe action is over.
* If you change the view on the start, you should reset is here, no matter the item has swiped or not.
*
* @param pos If the view is swiped, pos will be negative.
*/
void clearView(RecyclerView.ViewHolder viewHolder, int pos);
/**
* Called when item is swiped, the view is going to be removed from the adapter.
*/
void onItemSwiped(RecyclerView.ViewHolder viewHolder, int pos);
/**
* Draw on the empty edge when swipe moving
*
* @param canvas the empty edge's canvas
* @param viewHolder The ViewHolder which is being interacted by the User or it was
* interacted and simply animating to its original position
* @param dX The amount of horizontal displacement caused by user's action
* @param dY The amount of vertical displacement caused by user's action
* @param isCurrentlyActive True if this view is currently being controlled by the user or
* false it is simply animating back to its original state.
*/
void onItemSwipeMoving(Canvas canvas, RecyclerView.ViewHolder viewHolder, float dX, float dY, boolean isCurrentlyActive);
}
package com.chad.library.adapter.base.listener;
/**
* @author: limuyang
* @date: 2019-12-03
* @Description:
*/
public interface OnLoadMoreListener {
void onLoadMore();
}
package com.chad.library.adapter.base.listener;
/**
* @author: limuyang
* @date: 2019-12-03
* @Description:
*/
public interface OnUpFetchListener {
void onUpFetch();
}
package com.chad.library.adapter.base.listener;
import androidx.annotation.Nullable;
/**
* @author: limuyang
* @date: 2019-12-03
* @Description: UpFetch需要设置的接口。使用java定义,以兼容java写法
*/
public interface UpFetchListenerImp {
void setOnUpFetchListener(@Nullable OnUpFetchListener listener);
}
package com.chad.library.adapter.base.loadmore
import android.view.View
import android.view.ViewGroup
import com.chad.library.adapter.base.viewholder.BaseViewHolder
/**
*
* @author limuyang
*/
enum class LoadMoreStatus {
Complete, Loading, Fail, End
}
/**
* 继承此类,实行自定义loadMore视图
*/
abstract class BaseLoadMoreView {
/**
* 根布局
* @param parent ViewGroup
* @return View
*/
abstract fun getRootView(parent: ViewGroup): View
/**
* 布局中的 加载更多视图
* @param holder BaseViewHolder
* @return View
*/
abstract fun getLoadingView(holder: BaseViewHolder): View
/**
* 布局中的 加载完成布局
* @param holder BaseViewHolder
* @return View
*/
abstract fun getLoadComplete(holder: BaseViewHolder): View
/**
* 布局中的 加载结束布局
* @param holder BaseViewHolder
* @return View
*/
abstract fun getLoadEndView(holder: BaseViewHolder): View
/**
* 布局中的 加载失败布局
* @param holder BaseViewHolder
* @return View
*/
abstract fun getLoadFailView(holder: BaseViewHolder): View
/**
* 可重写此方式,实行自定义逻辑
* @param holder BaseViewHolder
* @param position Int
* @param loadMoreStatus LoadMoreStatus
*/
open fun convert(holder: BaseViewHolder, position: Int, loadMoreStatus: LoadMoreStatus) {
when (loadMoreStatus) {
LoadMoreStatus.Complete -> {
getLoadingView(holder).isVisible(false)
getLoadComplete(holder).isVisible(true)
getLoadFailView(holder).isVisible(false)
getLoadEndView(holder).isVisible(false)
}
LoadMoreStatus.Loading -> {
getLoadingView(holder).isVisible(true)
getLoadComplete(holder).isVisible(false)
getLoadFailView(holder).isVisible(false)
getLoadEndView(holder).isVisible(false)
}
LoadMoreStatus.Fail -> {
getLoadingView(holder).isVisible(false)
getLoadComplete(holder).isVisible(false)
getLoadFailView(holder).isVisible(true)
getLoadEndView(holder).isVisible(false)
}
LoadMoreStatus.End -> {
getLoadingView(holder).isVisible(false)
getLoadComplete(holder).isVisible(false)
getLoadFailView(holder).isVisible(false)
getLoadEndView(holder).isVisible(true)
}
}
}
private fun View.isVisible(visible: Boolean) {
this.visibility = if (visible) {
View.VISIBLE
} else {
View.GONE
}
}
}
package com.chad.library.adapter.base.loadmore
import android.view.View
import android.view.ViewGroup
import com.chad.library.R
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.chad.library.adapter.base.util.getItemView
class SimpleLoadMoreView : BaseLoadMoreView() {
override fun getRootView(parent: ViewGroup): View =
parent.getItemView(R.layout.brvah_quick_view_load_more)
override fun getLoadingView(holder: BaseViewHolder): View =
holder.getView(R.id.load_more_loading_view)
override fun getLoadComplete(holder: BaseViewHolder): View =
holder.getView(R.id.load_more_load_complete_view)
override fun getLoadEndView(holder: BaseViewHolder): View =
holder.getView(R.id.load_more_load_end_view)
override fun getLoadFailView(holder: BaseViewHolder): View =
holder.getView(R.id.load_more_load_fail_view)
}
\ No newline at end of file
package com.chad.library.adapter.base.module
import android.graphics.Canvas
import android.view.MotionEvent
import android.view.View
import android.view.View.OnLongClickListener
import android.view.View.OnTouchListener
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.chad.library.R
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.dragswipe.DragAndSwipeCallback
import com.chad.library.adapter.base.listener.DraggableListenerImp
import com.chad.library.adapter.base.listener.OnItemDragListener
import com.chad.library.adapter.base.listener.OnItemSwipeListener
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import java.util.*
/**
* @author: limuyang
* @date: 2019-12-05
* @Description:
*/
/**
* 需要【拖拽】功能的,[BaseQuickAdapter]继承此接口
*/
interface DraggableModule {
/**
* 重写此方法,返回自定义模块
* @param baseQuickAdapter BaseQuickAdapter<*, *>
* @return BaseExpandableModule
*/
fun addDraggableModule(baseQuickAdapter: BaseQuickAdapter<*, *>): BaseDraggableModule {
return BaseDraggableModule(baseQuickAdapter)
}
}
open class BaseDraggableModule(private val baseQuickAdapter: BaseQuickAdapter<*, *>) : DraggableListenerImp {
var isDragEnabled = false
var isSwipeEnabled = false
var toggleViewId = NO_TOGGLE_VIEW
lateinit var itemTouchHelper: ItemTouchHelper
lateinit var itemTouchHelperCallback: DragAndSwipeCallback
protected var mOnToggleViewTouchListener: OnTouchListener? = null
protected var mOnToggleViewLongClickListener: OnLongClickListener? = null
protected var mOnItemDragListener: OnItemDragListener? = null
protected var mOnItemSwipeListener: OnItemSwipeListener? = null
init {
initItemTouch()
}
private fun initItemTouch() {
itemTouchHelperCallback = DragAndSwipeCallback(this)
itemTouchHelper = ItemTouchHelper(itemTouchHelperCallback)
}
internal fun initView(holder: BaseViewHolder) {
if (isDragEnabled) {
if (hasToggleView()) {
val toggleView = holder.itemView.findViewById<View>(toggleViewId)
if (toggleView != null) {
toggleView.setTag(R.id.BaseQuickAdapter_viewholder_support, holder)
if (isDragOnLongPressEnabled) {
toggleView.setOnLongClickListener(mOnToggleViewLongClickListener)
} else {
toggleView.setOnTouchListener(mOnToggleViewTouchListener)
}
}
}
}
}
fun attachToRecyclerView(recyclerView: RecyclerView) {
itemTouchHelper.attachToRecyclerView(recyclerView)
}
/**
* Is there a toggle view which will trigger drag event.
*/
open fun hasToggleView(): Boolean {
return toggleViewId != NO_TOGGLE_VIEW
}
/**
* Set the drag event should be trigger on long press.
* Work when the toggleViewId has been set.
*
*/
open var isDragOnLongPressEnabled = true
set(value) {
field = value
if (value) {
mOnToggleViewTouchListener = null
mOnToggleViewLongClickListener = OnLongClickListener { v ->
if (isDragEnabled) {
itemTouchHelper.startDrag(v.getTag(R.id.BaseQuickAdapter_viewholder_support) as RecyclerView.ViewHolder)
}
true
}
} else {
mOnToggleViewTouchListener = OnTouchListener { v, event ->
if (event.action == MotionEvent.ACTION_DOWN && !isDragOnLongPressEnabled) {
if (isDragEnabled) {
itemTouchHelper.startDrag(v.getTag(R.id.BaseQuickAdapter_viewholder_support) as RecyclerView.ViewHolder)
}
true
} else {
false
}
}
mOnToggleViewLongClickListener = null
}
}
protected fun getViewHolderPosition(viewHolder: RecyclerView.ViewHolder): Int {
return viewHolder.adapterPosition - baseQuickAdapter.headerLayoutCount
}
/************************* Drag *************************/
open fun onItemDragStart(viewHolder: RecyclerView.ViewHolder) {
mOnItemDragListener?.onItemDragStart(viewHolder, getViewHolderPosition(viewHolder))
}
open fun onItemDragMoving(source: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder) {
val from = getViewHolderPosition(source)
val to = getViewHolderPosition(target)
if (inRange(from) && inRange(to)) {
if (from < to) {
for (i in from until to) {
Collections.swap(baseQuickAdapter.data, i, i + 1)
}
} else {
for (i in from downTo to + 1) {
Collections.swap(baseQuickAdapter.data, i, i - 1)
}
}
baseQuickAdapter.notifyItemMoved(source.adapterPosition, target.adapterPosition)
}
mOnItemDragListener?.onItemDragMoving(source, from, target, to)
}
open fun onItemDragEnd(viewHolder: RecyclerView.ViewHolder) {
mOnItemDragListener?.onItemDragEnd(viewHolder, getViewHolderPosition(viewHolder))
}
/************************* Swipe *************************/
open fun onItemSwipeStart(viewHolder: RecyclerView.ViewHolder) {
if (isSwipeEnabled) {
mOnItemSwipeListener?.onItemSwipeStart(viewHolder, getViewHolderPosition(viewHolder))
}
}
open fun onItemSwipeClear(viewHolder: RecyclerView.ViewHolder) {
if (isSwipeEnabled) {
mOnItemSwipeListener?.clearView(viewHolder, getViewHolderPosition(viewHolder))
}
}
open fun onItemSwiped(viewHolder: RecyclerView.ViewHolder) {
val pos = getViewHolderPosition(viewHolder)
if (inRange(pos)) {
baseQuickAdapter.data.removeAt(pos)
baseQuickAdapter.notifyItemRemoved(viewHolder.adapterPosition)
if (isSwipeEnabled) {
mOnItemSwipeListener?.onItemSwiped(viewHolder, pos)
}
}
}
open fun onItemSwiping(canvas: Canvas?, viewHolder: RecyclerView.ViewHolder?, dX: Float, dY: Float, isCurrentlyActive: Boolean) {
if (isSwipeEnabled) {
mOnItemSwipeListener?.onItemSwipeMoving(canvas, viewHolder, dX, dY, isCurrentlyActive)
}
}
private fun inRange(position: Int): Boolean {
return position >= 0 && position < baseQuickAdapter.data.size
}
/**
* 设置监听
* @param onItemDragListener OnItemDragListener?
*/
override fun setOnItemDragListener(onItemDragListener: OnItemDragListener?) {
this.mOnItemDragListener = onItemDragListener
}
override fun setOnItemSwipeListener(onItemSwipeListener: OnItemSwipeListener?) {
this.mOnItemSwipeListener = onItemSwipeListener
}
companion object {
private const val NO_TOGGLE_VIEW = 0
}
}
\ No newline at end of file
package com.chad.library.adapter.base.module
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.listener.LoadMoreListenerImp
import com.chad.library.adapter.base.listener.OnLoadMoreListener
import com.chad.library.adapter.base.loadmore.BaseLoadMoreView
import com.chad.library.adapter.base.loadmore.LoadMoreStatus
import com.chad.library.adapter.base.loadmore.SimpleLoadMoreView
import com.chad.library.adapter.base.viewholder.BaseViewHolder
/**
* @author: limuyang
* @date: 2019-11-29
* @Description: 向下加载更多
*/
/**
* 需要【向下加载更多】功能的,[BaseQuickAdapter]继承此接口
*/
interface LoadMoreModule {
/**
* 重写此方法,返回自定义模块
* @param baseQuickAdapter BaseQuickAdapter<*, *>
* @return BaseLoadMoreModule
*/
fun addLoadMoreModule(baseQuickAdapter: BaseQuickAdapter<*, *>): BaseLoadMoreModule {
return BaseLoadMoreModule(baseQuickAdapter)
}
}
object LoadMoreModuleConfig {
/**
* 设置全局的LodeMoreView
*/
@JvmStatic
var defLoadMoreView: BaseLoadMoreView = SimpleLoadMoreView()
}
/**
* 加载更多基类
*/
open class BaseLoadMoreModule(private val baseQuickAdapter: BaseQuickAdapter<*, *>) : LoadMoreListenerImp {
private var mLoadMoreListener: OnLoadMoreListener? = null
/** 不满一屏时,是否可以继续加载的标记位 */
private var mNextLoadEnable = true
var loadMoreStatus = LoadMoreStatus.Complete
private set
var isLoadEndMoreGone: Boolean = false
private set
/** 设置加载更多布局 */
var loadMoreView = LoadMoreModuleConfig.defLoadMoreView
/** 加载完成后是否允许点击 */
var enableLoadMoreEndClick = false
/** 是否打开自动加载更多 */
var isAutoLoadMore = true
/** 当自动加载开启,同时数据不满一屏时,是否继续执行自动加载更多 */
var isEnableLoadMoreIfNotFullPage = true
/**
* 预加载
*/
var preLoadNumber = 1
set(value) {
if (value > 1) {
field = value
}
}
/**
* 是否加载中
*/
val isLoading: Boolean
get() {
return loadMoreStatus == LoadMoreStatus.Loading
}
/**
* Gets to load more locations
*
* @return
*/
val loadMoreViewPosition: Int
get() {
if (baseQuickAdapter.hasEmptyView()) {
return -1
}
return baseQuickAdapter.let {
it.headerLayoutCount + it.data.size + it.footerLayoutCount
}
}
/**
* 是否打开加载更多
*/
var isEnableLoadMore = false
set(value) {
val oldHasLoadMore = hasLoadMoreView()
field = value
val newHasLoadMore = hasLoadMoreView()
if (oldHasLoadMore) {
if (!newHasLoadMore) {
baseQuickAdapter.notifyItemRemoved(loadMoreViewPosition)
}
} else {
if (newHasLoadMore) {
loadMoreStatus = LoadMoreStatus.Complete
baseQuickAdapter.notifyItemInserted(loadMoreViewPosition)
}
}
}
internal fun setupViewHolder(viewHolder: BaseViewHolder) {
viewHolder.itemView.setOnClickListener {
if (loadMoreStatus == LoadMoreStatus.Fail) {
loadMoreToLoading()
} else if (loadMoreStatus == LoadMoreStatus.Complete) {
loadMoreToLoading()
} else if (enableLoadMoreEndClick && loadMoreStatus == LoadMoreStatus.End) {
loadMoreToLoading()
}
}
}
/**
* The notification starts the callback and loads more
*/
fun loadMoreToLoading() {
if (loadMoreStatus == LoadMoreStatus.Loading) {
return
}
loadMoreStatus = LoadMoreStatus.Loading
baseQuickAdapter.notifyItemChanged(loadMoreViewPosition)
invokeLoadMoreListener()
}
fun hasLoadMoreView(): Boolean {
if (mLoadMoreListener == null || !isEnableLoadMore) {
return false
}
if (loadMoreStatus == LoadMoreStatus.End && isLoadEndMoreGone) {
return false
}
return baseQuickAdapter.data.isNotEmpty()
}
/**
* 自动加载数据
* @param position Int
*/
internal fun autoLoadMore(position: Int) {
if (!isAutoLoadMore) {
//如果不需要自动加载更多,直接返回
return
}
if (!hasLoadMoreView()) {
return
}
if (position < baseQuickAdapter.itemCount - preLoadNumber) {
return
}
if (loadMoreStatus != LoadMoreStatus.Complete) {
return
}
if (loadMoreStatus == LoadMoreStatus.Loading) {
return
}
if (!mNextLoadEnable) {
return
}
invokeLoadMoreListener()
}
/**
* 触发加载更多监听
*/
private fun invokeLoadMoreListener() {
loadMoreStatus = LoadMoreStatus.Loading
baseQuickAdapter.recyclerViewOrNull?.let {
it.post { mLoadMoreListener?.onLoadMore() }
} ?: mLoadMoreListener?.onLoadMore()
}
/**
* check if full page after [BaseQuickAdapter.setNewInstance] [BaseQuickAdapter.setList],
* if full, it will enable load more again.
*
* 用来检查数据是否满一屏,如果满足条件,再开启
*
*/
fun checkDisableLoadMoreIfNotFullPage() {
if (isEnableLoadMoreIfNotFullPage) {
return
}
// 先把标记位设置为false
mNextLoadEnable = false
val recyclerView = baseQuickAdapter.recyclerViewOrNull ?: return
val manager = recyclerView.layoutManager ?: return
if (manager is LinearLayoutManager) {
recyclerView.postDelayed({
if (isFullScreen(manager)) {
mNextLoadEnable = true
}
}, 50)
} else if (manager is StaggeredGridLayoutManager) {
recyclerView.postDelayed({
val positions = IntArray(manager.spanCount)
manager.findLastCompletelyVisibleItemPositions(positions)
val pos = getTheBiggestNumber(positions) + 1
if (pos != baseQuickAdapter.itemCount) {
mNextLoadEnable = true
}
}, 50)
}
}
private fun isFullScreen(llm: LinearLayoutManager): Boolean {
return (llm.findLastCompletelyVisibleItemPosition() + 1) != baseQuickAdapter.itemCount ||
llm.findFirstCompletelyVisibleItemPosition() != 0
}
private fun getTheBiggestNumber(numbers: IntArray?): Int {
var tmp = -1
if (numbers == null || numbers.isEmpty()) {
return tmp
}
for (num in numbers) {
if (num > tmp) {
tmp = num
}
}
return tmp
}
/**
* Refresh end, no more data
*
* @param gone if true gone the load more view
*/
@JvmOverloads
fun loadMoreEnd(gone: Boolean = false) {
if (!hasLoadMoreView()) {
return
}
// mNextLoadEnable = false
isLoadEndMoreGone = gone
loadMoreStatus = LoadMoreStatus.End
if (gone) {
baseQuickAdapter.notifyItemRemoved(loadMoreViewPosition)
} else {
baseQuickAdapter.notifyItemChanged(loadMoreViewPosition)
}
}
/**
* Refresh complete
*/
fun loadMoreComplete() {
if (!hasLoadMoreView()) {
return
}
// mNextLoadEnable = true
loadMoreStatus = LoadMoreStatus.Complete
baseQuickAdapter.notifyItemChanged(loadMoreViewPosition)
checkDisableLoadMoreIfNotFullPage()
}
/**
* Refresh failed
*/
fun loadMoreFail() {
if (!hasLoadMoreView()) {
return
}
loadMoreStatus = LoadMoreStatus.Fail
baseQuickAdapter.notifyItemChanged(loadMoreViewPosition)
}
/**
* 设置加载监听事件
* @param listener OnLoadMoreListener?
*/
override fun setOnLoadMoreListener(listener: OnLoadMoreListener?) {
this.mLoadMoreListener = listener
isEnableLoadMore = true
}
/**
* 重置状态
*/
internal fun reset() {
if (mLoadMoreListener != null) {
isEnableLoadMore = true
loadMoreStatus = LoadMoreStatus.Complete
}
}
}
\ No newline at end of file
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment