Browse Source

Merge branch 'master' of http://git.siwill.com/ifengouy/VisitorApp

ifengouy 2 years ago
parent
commit
e35c32d9f8
33 changed files with 2336 additions and 153 deletions
  1. 8 1
      app/build.gradle
  2. 0 2
      app/src/main/java/com/sunwin/visitorapp/BaseApplication.java
  3. 3 1
      app/src/main/java/com/sunwin/visitorapp/activity/SettingAc.java
  4. 19 0
      app/src/main/java/com/sunwin/visitorapp/db/BlackUserModel.java
  5. 1 1
      app/src/main/java/com/sunwin/visitorapp/db/DatabaseManager.java
  6. 117 0
      app/src/main/java/com/sunwin/visitorapp/fragment/BlackListManageFr.java
  7. 0 132
      app/src/main/java/com/sunwin/visitorapp/manage/OrmDBHelper.java
  8. 1 0
      app/src/main/java/com/sunwin/visitorapp/service/PullDataService.java
  9. 23 0
      app/src/main/java/com/sunwin/visitorapp/utils/FileUtil.java
  10. 561 0
      app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVGridItemDecoration.java
  11. 184 0
      app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVGridSpaceItemDecoration.java
  12. 20 0
      app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVItemDecorationConst.java
  13. 31 0
      app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVItemDecorationUtil.java
  14. 636 0
      app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVLinearItemDecoration.java
  15. 57 0
      app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVLinearSpaceItemDecoration.java
  16. 49 0
      app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVPaint.java
  17. 339 0
      app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVStickyHeadItemDecoration.java
  18. 15 0
      app/src/main/res/drawable/border_gary.xml
  19. 27 0
      app/src/main/res/drawable/folder_bg.xml
  20. BIN
      app/src/main/res/drawable/icon_image_select.png
  21. 38 0
      app/src/main/res/drawable/image_temp.xml
  22. 2 4
      app/src/main/res/layout/activity_home.xml
  23. 70 0
      app/src/main/res/layout/adapter_folder.xml
  24. 76 0
      app/src/main/res/layout/dialog_blacklist_add.xml
  25. 3 3
      app/src/main/res/layout/fr_blacklist.xml
  26. 2 2
      app/src/main/res/layout/fr_log_manage.xml
  27. 1 1
      app/src/main/res/layout/fr_page_manage.xml
  28. 2 2
      app/src/main/res/layout/fr_role_manage.xml
  29. 1 1
      app/src/main/res/layout/fr_system_option.xml
  30. 2 2
      app/src/main/res/layout/fr_user_manage.xml
  31. 43 0
      app/src/main/res/layout/item_blacklist.xml
  32. 4 1
      app/src/main/res/values/style.xml
  33. 1 0
      build.gradle

+ 8 - 1
app/build.gradle

@@ -7,13 +7,16 @@ android {
 
     defaultConfig {
         applicationId "com.sunwin.visitorapp"
-        minSdkVersion 17
+        minSdkVersion 19
+        //noinspection ExpiredTargetSdkVersion
         targetSdkVersion 26
         versionCode 1
         versionName "1.0"
         multiDexEnabled true
         vectorDrawables.useSupportLibrary = true
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+        multiDexEnabled true
     }
 
     buildTypes {
@@ -76,4 +79,8 @@ dependencies {
         exclude group: 'com.android.support'
     }
     implementation 'com.android.support:appcompat-v7:28.0.0'
+
+    implementation 'com.github.gzu-liyujiang.AndroidPicker:Common:4.1.9'
+    implementation 'com.github.gzu-liyujiang.AndroidPicker:FilePicker:4.1.9'
+    implementation 'com.github.gzu-liyujiang.AndroidPicker:ImagePicker:4.1.9'
 }

+ 0 - 2
app/src/main/java/com/sunwin/visitorapp/BaseApplication.java

@@ -3,9 +3,7 @@ package com.sunwin.visitorapp;
 import android.app.Application;
 import android.content.Context;
 
-import com.sunwin.visitorapp.manage.OrmDBHelper;
 import com.sunwin.visitorapp.utils.CacheUtil;
-import com.sunwin.visitorapp.utils.PresentationManager;
 import com.sunwin.visitorapp.utils.SystemTTS;
 import com.sunwin.visitorapp.utils.UniversalImageLoaderConfiguration;
 

+ 3 - 1
app/src/main/java/com/sunwin/visitorapp/activity/SettingAc.java

@@ -38,7 +38,9 @@ public class SettingAc extends BaseActivity {
             @Override
             public Fragment getItem(int position) {
                 switch (position){
-                    default:{
+                    default:
+                    case 0:
+                    {
                         return new BlackListManageFr();
 
                     }

+ 19 - 0
app/src/main/java/com/sunwin/visitorapp/db/BlackUserModel.java

@@ -0,0 +1,19 @@
+package com.sunwin.visitorapp.db;
+
+import com.litesuits.orm.db.annotation.PrimaryKey;
+import com.litesuits.orm.db.annotation.Table;
+import com.litesuits.orm.db.enums.AssignType;
+
+@Table("BlackUser")
+public class BlackUserModel {
+
+    // 指定自增,每个对象需要有一个主键
+    @PrimaryKey(AssignType.AUTO_INCREMENT)
+    private int id;
+
+    public String name;
+
+    public String id_card_code;
+
+    public String photo;
+}

+ 1 - 1
app/src/main/java/com/sunwin/visitorapp/db/DatabaseManager.java

@@ -26,7 +26,7 @@ public class DatabaseManager {
      *
      * @param t
      */
-    public <T> long insert(T t) {
+    public  <T> long insert(T t) {
         return liteOrm.save(t);
     }
 

+ 117 - 0
app/src/main/java/com/sunwin/visitorapp/fragment/BlackListManageFr.java

@@ -1,25 +1,142 @@
 package com.sunwin.visitorapp.fragment;
 
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.ImageView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
 import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
 
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.RequestBuilder;
+import com.github.gzuliyujiang.imagepicker.ActivityBuilder;
+import com.github.gzuliyujiang.imagepicker.ImagePicker;
+import com.github.gzuliyujiang.imagepicker.PickCallback;
 import com.sunwin.visitorapp.R;
+import com.sunwin.visitorapp.adapter.BaseRecyclerAdapter;
+import com.sunwin.visitorapp.adapter.BaseViewHolder;
+import com.sunwin.visitorapp.db.BlackUserModel;
+import com.sunwin.visitorapp.db.DatabaseManager;
+import com.sunwin.visitorapp.utils.FileUtil;
+import com.sunwin.visitorapp.utils.ToastUtils;
+import com.sunwin.visitorapp.view.itemDecoration.RVLinearItemDecoration;
+
+import java.io.File;
+import java.sql.Time;
+import java.util.ArrayList;
+import java.util.List;
+
+import timber.log.Timber;
 
 public class BlackListManageFr extends Fragment {
 
     private View root;
+    private Uri image_uri;
+    private BaseRecyclerAdapter<BlackUserModel> adapter = new BaseRecyclerAdapter<BlackUserModel>() {
+        @Override
+        protected View getLayoutView(ViewGroup parent, int viewType) {
+            return LayoutInflater.from(parent.getContext()).inflate(R.layout.item_blacklist,parent,false);
+        }
+
+        @SuppressLint("UseCompatLoadingForDrawables")
+        @Override
+        public void bind(int position, BlackUserModel data, BaseViewHolder holder, int viewType) {
+
+            Glide.with(root.getContext()).load(data.photo).error(getResources().getDrawable(R.drawable.image_temp)).into((ImageView) holder.getView(R.id.ImageHead));
+            holder.setText(R.id.TextName,data.name);
+            holder.setText(R.id.TextICCode,data.id_card_code);
+            holder.getView(R.id.ButtonDelete).setOnClickListener(v->{
+                DatabaseManager.getInstance().delete(data);
+                refreshData();
+            });
+        }
+    };
+
+
+    private void refreshData(){
+        adapter.setList(DatabaseManager.getInstance().getQueryAll(BlackUserModel.class));
+        Timber.e("adapter.getList().size() "+adapter.getList().size());
+    }
+    @SuppressLint("ResourceAsColor")
     @Nullable
     @Override
     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
 
         if(root==null)root = inflater.inflate(R.layout.fr_blacklist,container,false);
 
+
+        RecyclerView RecyclerBlackList = root.findViewById(R.id.RecyclerBlackList);
+        RecyclerBlackList.setAdapter(adapter);
+        RecyclerBlackList.setLayoutManager(new LinearLayoutManager(root.getContext(),LinearLayoutManager.VERTICAL,false));
+//        RecyclerBlackList.addItemDecoration(new RVLinearItemDecoration.Builder(root.getContext()).color(R.color.ColorGray).dashWidth(2).create());
+        refreshData();
+        root.findViewById(R.id.ButtonAdd).setOnClickListener(v->{
+            View dialog_root = inflater.inflate(R.layout.dialog_blacklist_add,container,false);
+            EditText editName = dialog_root.findViewById(R.id.EditName);
+            EditText editICCode = dialog_root.findViewById(R.id.EditICCode);
+            ImageView photo = dialog_root.findViewById(R.id.ImagePhoto);
+            AlertDialog dialog = new AlertDialog.Builder(root.getContext()).setTitle("添加黑名单")
+                    .setView(dialog_root)
+                    .create();
+            image_uri = null;
+            photo.setOnClickListener(pv->{
+                ImagePicker.getInstance().startGallery(this, false, new PickCallback() {
+                    @Override
+                    public void onPermissionDenied(String[] permissions, String message) {
+                        ToastUtils.showToast(message);
+                    }
+
+                    @Override
+                    public void onPickImage(@Nullable Uri imageUri) {
+                        super.onPickImage(imageUri);
+                        if(imageUri!=null){
+                            Glide.with(root.getContext()).load(imageUri).into(photo);
+                            image_uri = imageUri;
+                        }
+
+                    }
+                });
+            });
+            dialog_root.findViewById(R.id.ButtonSure).setOnClickListener(b->{
+
+                if(TextUtils.isEmpty(editName.getText().toString().trim())&&TextUtils.isEmpty(editICCode.getText().toString().trim())
+                &&image_uri==null){
+                    ToastUtils.showToast("至少一项不为空");
+                    return;
+                }
+                BlackUserModel model = new BlackUserModel();
+                model.name = editName.getText().toString().trim();
+                model.id_card_code = editICCode.getText().toString().trim();
+                model.photo = FileUtil.copyToFilesDir(root.getContext(), image_uri);
+                DatabaseManager.getInstance().insert(model);
+                refreshData();
+                dialog.dismiss();
+            });
+            dialog.show();
+        });
         return root;
     }
+
+
+
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        ImagePicker.getInstance().onActivityResult(this, requestCode, resultCode, data);
+    }
+
+
 }

+ 0 - 132
app/src/main/java/com/sunwin/visitorapp/manage/OrmDBHelper.java

@@ -1,132 +0,0 @@
-package com.sunwin.visitorapp.manage;
-
-
-import android.content.Context;
-import android.os.Environment;
-
-
-import com.litesuits.orm.LiteOrm;
-import com.litesuits.orm.db.assit.QueryBuilder;
-
-import java.io.File;
-import java.util.List;
-
-import timber.log.Timber;
-
-/**
- * Title:
- * Description:
- * <p>
- * Created by pei
- * Date: 2017/11/22
- */
-public class OrmDBHelper {
-
-    private static LiteOrm mLiteOrm;
-    private static final boolean isOpenOrmLog=true;// whether open the log
-
-    //orm数据库相关
-    private static final String ORM_DB_NAME="VisitorApp/VisitorApp.db";//orm数据库文件名,默认地址
-//    private final String ORM_DB_NAME= SDCardUtil.getInnerSDCardPath()+"student.db";//orm数据库文件名,自定义地址
-
-
-    public static void init(Context context){
-        File sdPath  = Environment.getExternalStorageDirectory();
-        mLiteOrm = LiteOrm.newSingleInstance(context, sdPath.getAbsolutePath()+File.pathSeparator+ORM_DB_NAME);
-        mLiteOrm.setDebugged(isOpenOrmLog); // open the log
-    }
-
-    /**获取orm数据库对象**/
-    public static  LiteOrm getOrmDataBase(){
-        return mLiteOrm;
-    }
-
-    //插入
-    public static <T> long insert(T t) {
-        return getOrmDataBase().insert(t);
-    }
-
-    //插入or更新
-    public static <T> long save(T t) {
-        return getOrmDataBase().save(t);
-    }
-
-    /***
-     * 更新
-     */
-    public static <T> void update(T t){
-        getOrmDataBase().update(t);
-    }
-
-    /**
-     * 插入所有记录
-     * @param list
-     */
-    public static <T> void insertAll(List<T> list) {
-        getOrmDataBase().save(list);
-    }
-
-    /**
-     * 查询所有
-     * @param cla
-     * @return
-     */
-    public static <T> List<T> getQueryAll(Class<T> cla) {
-        return getOrmDataBase().query(cla);
-    }
-
-    /**
-     * 查询  某字段 等于 Value的值
-     * @param cla
-     * @param field
-     * @param value
-     * @return
-     */
-    public static <T> List<T> getQueryByWhere(Class<T> cla, String field, Object[] value) {
-        return getOrmDataBase().<T>query(new QueryBuilder(cla).where(field + "=?", value));
-    }
-
-    /**
-     * 查询  某字段 等于 Value的值  可以指定从1-20,就是分页
-     * @param cla
-     * @param field
-     * @param value
-     * @param start
-     * @param length
-     * @return
-     */
-    public static <T> List<T> getQueryByWhereLength(Class<T> cla, String field, Object[] value, int start, int length) {
-        return getOrmDataBase().<T>query(new QueryBuilder(cla).where(field + "=?", value).limit(start, length));
-    }
-
-    /**
-     * 删除一个数据
-     * @param t
-     * @param <T>
-     */
-    public static <T> void delete( T t){
-        getOrmDataBase().delete( t ) ;
-    }
-
-    /**
-     * 删除一个表
-     * @param cla
-     * @param <T>
-     */
-    public static <T> void delete( Class<T> cla ){
-        getOrmDataBase().delete( cla );
-    }
-
-    /**
-     * 删除集合中的数据
-     * @param list
-     * @param <T>
-     */
-    public static <T> void deleteList( List<T> list ){
-        getOrmDataBase().delete( list ) ;
-    }
-
-
-
-}
-

+ 1 - 0
app/src/main/java/com/sunwin/visitorapp/service/PullDataService.java

@@ -7,6 +7,7 @@ import androidx.annotation.NonNull;
 import androidx.core.app.JobIntentService;
 
 import com.sunwin.visitorapp.db.DatabaseManager;
+import com.sunwin.visitorapp.face.CameraPreviewData;
 import com.sunwin.visitorapp.model.DepartmentResult;
 import com.sunwin.visitorapp.model.NetInfo;
 import com.sunwin.visitorapp.net.ApiService;

+ 23 - 0
app/src/main/java/com/sunwin/visitorapp/utils/FileUtil.java

@@ -7,17 +7,40 @@ import android.graphics.ImageFormat;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.YuvImage;
+import android.net.Uri;
 import android.util.Log;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 
 public class FileUtil {
     private static final String TAG = "FileUtil";
 
 
+    public static String copyToFilesDir(Context context, Uri uri){
+        try {
+
+            InputStream inputStream = context.getContentResolver().openInputStream(uri);
+            String lastSegment = uri.getLastPathSegment();
+            File outFile = new File(context.getFilesDir(),lastSegment);
+            OutputStream outputStream = new FileOutputStream(outFile);
+            byte[] bytes = new byte[16];
+            while ((inputStream.read(bytes))!=-1){
+                outputStream.write(bytes);
+            }
+            inputStream.close();
+            outputStream.close();
+            return outFile.getAbsolutePath();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
 
 
     public static void copyFilesFromAssets(Context context, String assetsPath, String savePath){

+ 561 - 0
app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVGridItemDecoration.java

@@ -0,0 +1,561 @@
+package com.sunwin.visitorapp.view.itemDecoration;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * Grid mode ItemDecoration
+ * Created by Eminem Lo on 2019-09-24.
+ * email: arjinmc@hotmail.com
+ */
+public class RVGridItemDecoration extends RecyclerView.ItemDecoration {
+
+    /**
+     * default decoration color
+     */
+    private static final String DEFAULT_COLOR = "#bdbdbd";
+
+    /**
+     * the color for the diver
+     */
+    private int mColor;
+    /**
+     * horizontalSpacing
+     */
+    private int mHorizontalSpacing = 0;
+    /**
+     * VerticalSpacing
+     */
+    private int mVerticalSpacing = 0;
+
+    /**
+     * show border
+     */
+    private boolean mBorderVisible;
+
+    private Paint mPaint;
+
+    public void setParams(Param param) {
+
+        mColor = param.color;
+        mHorizontalSpacing = param.horizontalSpacing;
+        mVerticalSpacing = param.verticalSpacing;
+        mBorderVisible = param.borderVisible;
+
+        initPaint();
+    }
+
+    private void initPaint() {
+
+        mPaint = new Paint();
+        mPaint.setColor(mColor);
+        mPaint.setStyle(Paint.Style.STROKE);
+    }
+
+    @Override
+    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
+
+        if (parent.getChildCount() == 0) {
+            return;
+        }
+        if (parent.getLayoutManager() instanceof GridLayoutManager) {
+
+            GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
+            if (layoutManager.getOrientation() == RecyclerView.HORIZONTAL) {
+                try {
+                    throw new IllegalAccessException("RVGridSpaceItemDecoration only support GridLayoutManager Vertical");
+                } catch (IllegalAccessException e) {
+                    e.printStackTrace();
+                    return;
+                }
+            }
+            int position = parent.getChildAdapterPosition(view);
+            int spanCount = layoutManager.getSpanCount();
+            GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) view.getLayoutParams();
+            int column = lp.getSpanIndex();
+            getGridItemOffsets(outRect, position, column, spanCount);
+        } else {
+            try {
+                throw new IllegalAccessException("RVGridSpaceItemDecoration only support GridLayoutManager Vertical");
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+                return;
+            }
+        }
+    }
+
+    @Override
+    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
+        super.onDraw(c, parent, state);
+        if (parent.getChildCount() == 0) {
+            return;
+        }
+        drawGrid(c, parent);
+    }
+
+    /**
+     * draw grid decoration
+     *
+     * @param c
+     * @param parent
+     */
+    private void drawGrid(Canvas c, RecyclerView parent) {
+
+        int childrenCount = parent.getChildCount();
+        int columnSize = ((GridLayoutManager) parent.getLayoutManager()).getSpanCount();
+        int itemSize = parent.getAdapter().getItemCount();
+
+        for (int i = 0; i < childrenCount; i++) {
+            View childView = parent.getChildAt(i);
+            int myT = childView.getTop();
+            int myB = childView.getBottom();
+            int myL = childView.getLeft();
+            int myR = childView.getRight();
+            int viewPosition = parent.getLayoutManager().getPosition(childView);
+
+            //when columnSize/spanCount is One
+            if (columnSize == 1) {
+
+
+                if (isFirstGridRow(viewPosition, columnSize)) {
+
+                    //draw left border
+                    if (mBorderVisible) {
+                        mPaint.setStrokeWidth(mHorizontalSpacing);
+                        Path path = new Path();
+                        path.moveTo(myL - mHorizontalSpacing / 2, myT);
+                        path.lineTo(myL - mHorizontalSpacing / 2, myB);
+                        c.drawPath(path, mPaint);
+                    }
+                    //draw top border
+                    if (mBorderVisible) {
+                        mPaint.setStrokeWidth(mVerticalSpacing);
+                        Path path = new Path();
+                        path.moveTo(myL - mHorizontalSpacing, myT - mVerticalSpacing / 2);
+                        path.lineTo(myR + mHorizontalSpacing, myT - mVerticalSpacing / 2);
+                        c.drawPath(path, mPaint);
+                    }
+                    //draw right border
+                    if (mBorderVisible) {
+                        mPaint.setStrokeWidth(mHorizontalSpacing);
+                        Path path = new Path();
+                        path.moveTo(myR + mHorizontalSpacing / 2, myT);
+                        path.lineTo(myR + mHorizontalSpacing / 2, myB);
+                        c.drawPath(path, mPaint);
+                    }
+
+                    //not first row
+                } else {
+
+                    //draw left border
+                    if (mBorderVisible) {
+                        mPaint.setStrokeWidth(mHorizontalSpacing);
+                        Path path = new Path();
+                        path.moveTo(myL - mHorizontalSpacing / 2
+                                , myT - mVerticalSpacing);
+                        path.lineTo(myL - mHorizontalSpacing / 2
+                                , myB);
+                        c.drawPath(path, mPaint);
+                    }
+                    //draw right border
+                    if (mBorderVisible) {
+                        Path path = new Path();
+                        mPaint.setStrokeWidth(mHorizontalSpacing);
+                        path.moveTo(myR + mHorizontalSpacing / 2
+                                , myT);
+                        path.lineTo(myR + mHorizontalSpacing / 2
+                                , myB);
+                        c.drawPath(path, mPaint);
+                    }
+
+                }
+
+                if (isLastGridRow(viewPosition, itemSize, columnSize)) {
+                    //draw bottom border
+                    if (mBorderVisible) {
+                        mPaint.setStrokeWidth(mVerticalSpacing);
+                        Path path = new Path();
+                        path.moveTo(myL - mHorizontalSpacing
+                                , myB + mVerticalSpacing / 2);
+                        path.lineTo(myR + mHorizontalSpacing
+                                , myB + mVerticalSpacing / 2);
+                        c.drawPath(path, mPaint);
+                    }
+                } else {
+                    mPaint.setStrokeWidth(mVerticalSpacing);
+                    Path path = new Path();
+                    path.moveTo(myL
+                            , myB + mVerticalSpacing / 2);
+                    path.lineTo(myR + mHorizontalSpacing
+                            , myB + mVerticalSpacing / 2);
+                    c.drawPath(path, mPaint);
+                }
+
+                //when columnSize/spanCount is Not One
+            } else {
+                if (isFirstGridColumn(viewPosition, columnSize) && isFirstGridRow(viewPosition, columnSize)) {
+
+                    //draw left border
+                    if (mBorderVisible) {
+                        mPaint.setStrokeWidth(mHorizontalSpacing);
+                        Path path = new Path();
+                        path.moveTo(myL - mHorizontalSpacing / 2
+                                , myT - mVerticalSpacing);
+                        path.lineTo(myL - mHorizontalSpacing / 2
+                                , myB);
+                        c.drawPath(path, mPaint);
+                    }
+
+                    //draw top border
+                    if (mBorderVisible) {
+                        mPaint.setStrokeWidth(mVerticalSpacing);
+                        Path path = new Path();
+                        path.moveTo(myL
+                                , myT - mVerticalSpacing / 2);
+                        path.lineTo(myR + mHorizontalSpacing / 2
+                                , myT - mVerticalSpacing / 2);
+                        c.drawPath(path, mPaint);
+
+                    }
+
+                    if (itemSize == 1) {
+                        //draw right border
+                        if (mBorderVisible) {
+                            mPaint.setStrokeWidth(mHorizontalSpacing);
+                            Path path = new Path();
+                            path.moveTo(myR + mHorizontalSpacing / 2
+                                    , myT - mHorizontalSpacing);
+                            path.lineTo(myR + mHorizontalSpacing / 2
+                                    , myB);
+                            c.drawPath(path, mPaint);
+                        }
+                    } else {
+                        mPaint.setStrokeWidth(mHorizontalSpacing);
+                        Path path = new Path();
+                        path.moveTo(myR + mHorizontalSpacing / 2
+                                , myT);
+                        path.lineTo(myR + mHorizontalSpacing / 2
+                                , myB);
+                        c.drawPath(path, mPaint);
+                    }
+
+                } else if (isFirstGridRow(viewPosition, columnSize)) {
+
+                    //draw top border
+                    if (mBorderVisible) {
+
+                        if (isLastGridColumn(viewPosition, itemSize, columnSize)) {
+                            mPaint.setStrokeWidth(mVerticalSpacing);
+                            Path path = new Path();
+                            path.moveTo(myL - mHorizontalSpacing / 2
+                                    , myT - mVerticalSpacing / 2);
+                            path.lineTo(myR
+                                    , myT - mVerticalSpacing / 2);
+                            c.drawPath(path, mPaint);
+                        } else {
+                            mPaint.setStrokeWidth(mVerticalSpacing);
+                            Path path = new Path();
+                            path.moveTo(myL - mHorizontalSpacing / 2
+                                    , myT - mVerticalSpacing / 2);
+                            path.lineTo(myR + mHorizontalSpacing / 2
+                                    , myT - mVerticalSpacing / 2);
+                            c.drawPath(path, mPaint);
+                        }
+
+                    }
+
+                    if (isLastGridColumn(viewPosition, itemSize, columnSize)) {
+                        mPaint.setStrokeWidth(mHorizontalSpacing);
+                        //draw right border
+                        if (mBorderVisible) {
+
+                            int alterY = 0;
+                            if (isLastSecondGridRowNotDivided(viewPosition, itemSize, columnSize)) {
+                                alterY = mHorizontalSpacing;
+                            }
+                            Path path = new Path();
+                            path.moveTo(myR + mHorizontalSpacing / 2
+                                    , myT - mHorizontalSpacing - mVerticalSpacing);
+                            path.lineTo(myR + mHorizontalSpacing / 2
+                                    , myB + alterY + mVerticalSpacing / 2);
+                            c.drawPath(path, mPaint);
+                        }
+                    } else {
+                        mPaint.setStrokeWidth(mHorizontalSpacing);
+                        Path path = new Path();
+                        path.moveTo(myR + mHorizontalSpacing / 2
+                                , myT);
+                        path.lineTo(myR + mHorizontalSpacing / 2
+                                , myB);
+                        c.drawPath(path, mPaint);
+                    }
+
+                } else if (isFirstGridColumn(viewPosition, columnSize)) {
+
+                    //draw left border
+                    if (mBorderVisible) {
+                        mPaint.setStrokeWidth(mHorizontalSpacing);
+                        Path path = new Path();
+                        path.moveTo(myL - mHorizontalSpacing / 2
+                                , myT);
+                        path.lineTo(myL - mHorizontalSpacing / 2
+                                , myB);
+                        c.drawPath(path, mPaint);
+                    }
+
+                    mPaint.setStrokeWidth(mHorizontalSpacing);
+                    Path path = new Path();
+                    path.moveTo(myR + mHorizontalSpacing / 2
+                            , myT);
+                    path.lineTo(myR + mHorizontalSpacing / 2
+                            , myB);
+                    c.drawPath(path, mPaint);
+
+                } else {
+
+                    mPaint.setStrokeWidth(mHorizontalSpacing);
+
+                    if (isLastGridColumn(viewPosition, itemSize, columnSize)) {
+
+                        //draw right border
+                        if (mBorderVisible) {
+
+                            int alterY = 0;
+                            if (isLastSecondGridRowNotDivided(viewPosition, itemSize, columnSize)) {
+                                alterY = mVerticalSpacing / 2;
+                            }
+                            Path path = new Path();
+                            path.moveTo(myR + mHorizontalSpacing / 2
+                                    , myT - mVerticalSpacing / 2);
+                            path.lineTo(myR + mHorizontalSpacing / 2
+                                    , myB + alterY + mVerticalSpacing / 2);
+                            c.drawPath(path, mPaint);
+                        }
+                    } else {
+                        mPaint.setStrokeWidth(mHorizontalSpacing);
+                        Path path = new Path();
+                        path.moveTo(myR + mHorizontalSpacing / 2
+                                , myT);
+                        path.lineTo(myR + mHorizontalSpacing / 2
+                                , myB);
+                        c.drawPath(path, mPaint);
+                    }
+                }
+
+                //bottom line
+                if (isLastGridRow(viewPosition, itemSize, columnSize)) {
+                    //draw bottom border
+                    if (mBorderVisible) {
+                        mPaint.setStrokeWidth(mHorizontalSpacing);
+                        if (itemSize == 1) {
+                            Path path = new Path();
+                            path.moveTo(myL - mHorizontalSpacing
+                                    , myB + mHorizontalSpacing / 2);
+                            path.lineTo(myR + mHorizontalSpacing
+                                    , myB + mHorizontalSpacing / 2);
+                            c.drawPath(path, mPaint);
+                        } else if (isLastGridColumn(viewPosition, itemSize, columnSize)) {
+                            Path path = new Path();
+                            path.moveTo(myL - mHorizontalSpacing
+                                    , myB + mHorizontalSpacing / 2);
+                            path.lineTo(myR + mHorizontalSpacing
+                                    , myB + mHorizontalSpacing / 2);
+                            c.drawPath(path, mPaint);
+                        } else if (isFirstGridColumn(viewPosition, columnSize)) {
+                            mPaint.setStrokeWidth(mVerticalSpacing);
+                            Path path = new Path();
+                            path.moveTo(myL - mHorizontalSpacing
+                                    , myB + mVerticalSpacing / 2);
+                            path.lineTo(myR + mHorizontalSpacing
+                                    , myB + mVerticalSpacing / 2);
+                            c.drawPath(path, mPaint);
+                        } else {
+                            mPaint.setStrokeWidth(mVerticalSpacing);
+                            Path path = new Path();
+                            path.moveTo(myL
+                                    , myB + mVerticalSpacing / 2);
+                            path.lineTo(myR + mHorizontalSpacing
+                                    , myB + mVerticalSpacing / 2);
+                            c.drawPath(path, mPaint);
+                        }
+
+                    }
+                } else {
+                    mPaint.setStrokeWidth(mVerticalSpacing);
+                    Path path = new Path();
+                    path.moveTo(myL - mHorizontalSpacing
+                            , myB + mVerticalSpacing / 2);
+                    path.lineTo(myR
+                            , myB + mVerticalSpacing / 2);
+                    c.drawPath(path, mPaint);
+                }
+            }
+
+        }
+
+    }
+
+    /**
+     * check if is one of the first columns
+     *
+     * @param position
+     * @param columnSize
+     * @return
+     */
+    private boolean isFirstGridColumn(int position, int columnSize) {
+
+        return position % columnSize == 0;
+    }
+
+    /**
+     * check if is one of the last columns
+     *
+     * @param position
+     * @param columnSize
+     * @return
+     */
+    private boolean isLastGridColumn(int position, int itemSize, int columnSize) {
+        boolean isLast = false;
+        if ((position + 1) % columnSize == 0) {
+            isLast = true;
+        }
+        return isLast;
+    }
+
+    /**
+     * check if is the first row of th grid
+     *
+     * @param position
+     * @param columnSize
+     * @return
+     */
+    private boolean isFirstGridRow(int position, int columnSize) {
+        return position < columnSize;
+    }
+
+    /**
+     * check if is the last row of the grid
+     *
+     * @param position
+     * @param itemSize
+     * @param columnSize
+     * @return
+     */
+    private boolean isLastGridRow(int position, int itemSize, int columnSize) {
+        int temp = itemSize % columnSize;
+        if (temp == 0 && position >= itemSize - columnSize) {
+            return true;
+        } else if (position >= itemSize / columnSize * columnSize) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * check if is the last second row of the grid when the itemSize cannot be divided by columnSize
+     *
+     * @param position
+     * @param itemSize
+     * @param columnSize
+     * @return
+     */
+    private boolean isLastSecondGridRowNotDivided(int position, int itemSize, int columnSize) {
+        int temp = itemSize % columnSize;
+        if (temp != 0 && itemSize - 1 - temp == position) {
+            return true;
+        }
+        return false;
+    }
+
+    private void getGridItemOffsets(Rect outRect, int position, int column, int spanCount) {
+
+        if (mBorderVisible) {
+            outRect.left = mHorizontalSpacing * (spanCount - column) / spanCount;
+            outRect.right = mHorizontalSpacing * (column + 1) / spanCount;
+            if (position < spanCount) {
+                outRect.top = mVerticalSpacing;
+            }
+            outRect.bottom = mVerticalSpacing;
+        } else {
+            outRect.left = mHorizontalSpacing * column / spanCount;
+            outRect.right = mHorizontalSpacing * (spanCount - 1 - column) / spanCount;
+            if (position >= spanCount) {
+                outRect.top = mVerticalSpacing;
+            }
+        }
+    }
+
+    public static class Builder {
+
+        private Param params;
+
+        public Builder(Context context) {
+
+            params = new Param();
+        }
+
+        public RVGridItemDecoration create() {
+            RVGridItemDecoration recyclerViewItemDecoration = new RVGridItemDecoration();
+            recyclerViewItemDecoration.setParams(params);
+            return recyclerViewItemDecoration;
+        }
+
+
+        public Builder color(@ColorInt int color) {
+            params.color = color;
+            return this;
+        }
+
+        public Builder color(String color) {
+            if (RVItemDecorationUtil.isColorStringWithoutAlpha(color)) {
+                params.color = Color.parseColor(color);
+            }
+            return this;
+        }
+
+        public Builder horizontalSpacing(int spacing) {
+            if (spacing < 0) {
+                spacing = 0;
+            }
+            if (spacing % 2 != 0) {
+                spacing += 1;
+            }
+            params.horizontalSpacing = spacing;
+            return this;
+        }
+
+        public Builder verticalSpacing(int spacing) {
+            if (spacing < 0) {
+                spacing = 0;
+            }
+            if (spacing % 2 != 0) {
+                spacing += 1;
+            }
+            params.verticalSpacing = spacing;
+            return this;
+        }
+
+        public Builder borderVisible(boolean borderVisible) {
+            params.borderVisible = borderVisible;
+            return this;
+        }
+    }
+
+    private static class Param {
+
+        public int color = Color.parseColor(DEFAULT_COLOR);
+        public int horizontalSpacing = 0;
+        public int verticalSpacing = 0;
+        public boolean borderVisible;
+    }
+}

+ 184 - 0
app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVGridSpaceItemDecoration.java

@@ -0,0 +1,184 @@
+package com.sunwin.visitorapp.view.itemDecoration;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.StaggeredGridLayoutManager;
+
+/**
+ * RecyclerView Grid Space Decoration
+ * Created by Eminem Lo on 2019-09-24.
+ * email: arjinmc@hotmail.com
+ */
+public class RVGridSpaceItemDecoration extends RecyclerView.ItemDecoration {
+
+    private int mHorizontalSpacing;
+    private int mVerticalSpacing;
+
+    /**
+     * show edge
+     */
+    private boolean mBorderVisible;
+
+    public RVGridSpaceItemDecoration() {
+
+    }
+
+    public RVGridSpaceItemDecoration(int spacing, boolean borderVisible) {
+        mHorizontalSpacing = spacing;
+        mVerticalSpacing = spacing;
+        mBorderVisible = borderVisible;
+    }
+
+    public RVGridSpaceItemDecoration(int horizontalSpacing, int verticalSpacing, boolean borderVisible) {
+        mHorizontalSpacing = horizontalSpacing;
+        mVerticalSpacing = verticalSpacing;
+        mBorderVisible = borderVisible;
+    }
+
+    public RVGridSpaceItemDecoration(int spacing) {
+        mHorizontalSpacing = spacing;
+        mVerticalSpacing = spacing;
+    }
+
+    public RVGridSpaceItemDecoration(int horizontalSpacing, int verticalSpacing) {
+        mHorizontalSpacing = horizontalSpacing;
+        mVerticalSpacing = verticalSpacing;
+    }
+
+    public void setParams(Param params) {
+        if (params.margin > 0) {
+            mHorizontalSpacing = params.margin;
+            mVerticalSpacing = params.margin;
+        } else {
+            mHorizontalSpacing = params.marginHorizontal;
+            mVerticalSpacing = params.marginVertical;
+        }
+    }
+
+    @Override
+    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+
+        if (parent.getChildCount() == 0) {
+            return;
+        }
+
+        if (parent.getLayoutManager() instanceof GridLayoutManager) {
+
+            GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
+            if (layoutManager.getOrientation() == RecyclerView.HORIZONTAL) {
+                try {
+                    throw new IllegalAccessException("RVGridSpaceItemDecoration only support GridLayoutManager/StaggeredGridLayoutManager Vertical");
+                } catch (IllegalAccessException e) {
+                    e.printStackTrace();
+                    return;
+                }
+            }
+            int position = parent.getChildAdapterPosition(view);
+            int spanCount = layoutManager.getSpanCount();
+            GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) view.getLayoutParams();
+            int column = lp.getSpanIndex();
+            getGridItemOffsets(outRect, position, column, spanCount);
+        } else if (parent.getLayoutManager() instanceof StaggeredGridLayoutManager) {
+            StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) parent.getLayoutManager();
+            if (layoutManager.getOrientation() == RecyclerView.HORIZONTAL) {
+                try {
+                    throw new IllegalAccessException("RVGridSpaceItemDecoration only support GridLayoutManager/StaggeredGridLayoutManager Vertical");
+                } catch (IllegalAccessException e) {
+                    e.printStackTrace();
+                    return;
+                }
+            }
+
+            int position = parent.getChildAdapterPosition(view);
+            int spanCount = layoutManager.getSpanCount();
+            StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
+            int column = lp.getSpanIndex();
+            getGridItemOffsets(outRect, position, column, spanCount);
+        } else {
+            try {
+                throw new IllegalAccessException("RVGridSpaceItemDecoration only support GridLayoutManager/StaggeredGridLayoutManager");
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void getGridItemOffsets(Rect outRect, int position, int column, int spanCount) {
+
+        if (mBorderVisible) {
+            outRect.left = mHorizontalSpacing * (spanCount - column) / spanCount;
+            outRect.right = mHorizontalSpacing * (column + 1) / spanCount;
+            if (position < spanCount) {
+                outRect.top = mVerticalSpacing;
+            }
+            outRect.bottom = mVerticalSpacing;
+        } else {
+            outRect.left = mHorizontalSpacing * column / spanCount;
+            outRect.right = mHorizontalSpacing * (spanCount - 1 - column) / spanCount;
+            if (position >= spanCount) {
+                outRect.top = mVerticalSpacing;
+            }
+        }
+    }
+
+    public static class Builder {
+
+        private Context context;
+        private Param param;
+
+        public Builder(Context context) {
+            this.context = context;
+            param = new Param();
+        }
+
+        public Builder margin(int margin) {
+            if (margin < 0) {
+                margin = 0;
+            }
+            param.margin = margin;
+            return this;
+        }
+
+        public Builder marginHorizontal(int margin) {
+            if (margin < 0) {
+                margin = 0;
+            }
+            param.marginHorizontal = margin;
+            return this;
+        }
+
+        public Builder marginVertical(int margin) {
+            if (margin < 0) {
+                margin = 0;
+            }
+            param.marginVertical = margin;
+            return this;
+        }
+
+        public Builder borderVisible(boolean visible) {
+            param.borderVisible = visible;
+            return this;
+        }
+
+        public RVGridSpaceItemDecoration create() {
+            RVGridSpaceItemDecoration RVGridSpaceItemDecoration =
+                    new RVGridSpaceItemDecoration();
+            RVGridSpaceItemDecoration.setParams(param);
+            return RVGridSpaceItemDecoration;
+
+        }
+
+    }
+
+    public static class Param {
+
+        public int margin;
+        public int marginHorizontal;
+        public int marginVertical;
+        public boolean borderVisible;
+    }
+}

+ 20 - 0
app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVItemDecorationConst.java

@@ -0,0 +1,20 @@
+package com.sunwin.visitorapp.view.itemDecoration;
+
+/**
+ * Constant of RecyclerView ItemDecoration
+ * Created by Eminem Lo on 2017/9/8.
+ * email: arjinmc@hotmail.com
+ */
+
+public final class RVItemDecorationConst {
+
+    /**
+     * mode for direction
+     * draw the itemdrecoration orientation
+     */
+
+    public static final int MODE_UNKNOWN = -1;
+    public static final int MODE_HORIZONTAL = 0;
+    public static final int MODE_VERTICAL = 1;
+    public static final int MODE_GRID = 2;
+}

+ 31 - 0
app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVItemDecorationUtil.java

@@ -0,0 +1,31 @@
+package com.sunwin.visitorapp.view.itemDecoration;
+
+import java.util.regex.Pattern;
+
+/**
+ * RecyclerView ItemDecoration Util
+ * Created by Eminem Lo on 2019-09-24.
+ * email: arjinmc@hotmail.com
+ */
+final class RVItemDecorationUtil {
+
+    /**
+     * check is a color string like #xxxxxx or #xxxxxxxx
+     *
+     * @param colorStr
+     * @return
+     */
+    public static boolean isColorString(String colorStr) {
+        return Pattern.matches("^#([0-9a-fA-F]{6}||[0-9a-fA-F]{8})$", colorStr);
+    }
+
+    /**
+     * check is a color string like #xxxxxx
+     *
+     * @param colorStr
+     * @return
+     */
+    public static boolean isColorStringWithoutAlpha(String colorStr) {
+        return Pattern.matches("^#[0-9a-fA-F]{6}$", colorStr);
+    }
+}

+ 636 - 0
app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVLinearItemDecoration.java

@@ -0,0 +1,636 @@
+package com.sunwin.visitorapp.view.itemDecoration;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.DashPathEffect;
+import android.graphics.NinePatch;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PathEffect;
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.DrawableRes;
+import androidx.collection.ArrayMap;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * RecycleView item decoration
+ * Created by Eminem Lo on 24/11/15.
+ * Email arjinmc@hotmail.com
+ */
+public class RVLinearItemDecoration extends RecyclerView.ItemDecoration {
+
+    /**
+     * default decoration color
+     */
+    private static final String DEFAULT_COLOR = "#bdbdbd";
+
+    /**
+     * image resource id for R.java
+     */
+    private int mDrawableRid = 0;
+    /**
+     * decoration color
+     */
+    private int mColor = Color.parseColor(DEFAULT_COLOR);
+    /**
+     * decoration thickness
+     */
+    private int mThickness;
+    /**
+     * decoration dash with
+     */
+    private int mDashWidth = 0;
+    /**
+     * decoration dash gap
+     */
+    private int mDashGap = 0;
+    private boolean mFirstLineVisible;
+    private boolean mLastLineVisible;
+    private int mPaddingStart = 0;
+    private int mPaddingEnd = 0;
+
+    /***
+     * ignore the item types which won't be drew item decoration
+     * only for mode horizontal and vertical
+     */
+    private ArrayMap<Integer, Integer> mIgnoreTypes;
+
+    /**
+     * direction mode for decoration
+     */
+    private int mMode;
+
+    private Paint mPaint;
+
+    private Bitmap mBmp;
+    private NinePatch mNinePatch;
+    /**
+     * choose the real thickness for image or thickness
+     */
+    private int mCurrentThickness;
+    /**
+     * sign for if the resource image is a ninepatch image
+     */
+    private boolean hasNinePatch = false;
+    /**
+     * sign for if has get the parent RecyclerView LayoutManager mode
+     */
+    private boolean hasGetParentLayoutMode = false;
+    private Context mContext;
+
+    public RVLinearItemDecoration() {
+    }
+
+    public void setParams(Context context, Param params) {
+
+        this.mContext = context;
+
+        this.mDrawableRid = params.drawableRid;
+        this.mColor = params.color;
+        this.mThickness = params.thickness;
+        this.mDashGap = params.dashGap;
+        this.mDashWidth = params.dashWidth;
+        this.mPaddingStart = params.paddingStart;
+        this.mPaddingEnd = params.paddingEnd;
+        this.mFirstLineVisible = params.firstLineVisible;
+        this.mLastLineVisible = params.lastLineVisible;
+        if (params.ignoreTypes != null && params.ignoreTypes.length != 0) {
+            this.mIgnoreTypes = new ArrayMap<>();
+            int ignoreTypeSize = params.ignoreTypes.length;
+            for (int i = 0; i < ignoreTypeSize; i++) {
+                this.mIgnoreTypes.put(params.ignoreTypes[i], params.ignoreTypes[i]);
+            }
+        }
+
+    }
+
+    private void initPaint(Context context) {
+
+        this.mBmp = BitmapFactory.decodeResource(context.getResources(), mDrawableRid);
+        if (mBmp != null) {
+
+            if (mBmp.getNinePatchChunk() != null) {
+                hasNinePatch = true;
+                mNinePatch = new NinePatch(mBmp, mBmp.getNinePatchChunk(), null);
+            }
+
+            if (mMode == RVItemDecorationConst.MODE_HORIZONTAL) {
+                mCurrentThickness = mThickness == 0 ? mBmp.getHeight() : mThickness;
+            }
+            if (mMode == RVItemDecorationConst.MODE_VERTICAL) {
+                mCurrentThickness = mThickness == 0 ? mBmp.getWidth() : mThickness;
+            }
+        } else {
+            mCurrentThickness = mThickness;
+        }
+
+        mPaint = new Paint();
+        mPaint.setColor(mColor);
+        mPaint.setStyle(Paint.Style.STROKE);
+        mPaint.setStrokeWidth(mCurrentThickness);
+    }
+
+
+    @Override
+    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+
+        if (parent.getAdapter() == null || parent.getChildCount() == 0) {
+            return;
+        }
+        mPaint.setColor(mColor);
+        if (mMode == RVItemDecorationConst.MODE_HORIZONTAL) {
+            drawHorizontal(c, parent);
+        } else if (mMode == RVItemDecorationConst.MODE_VERTICAL) {
+            drawVertical(c, parent);
+        }
+    }
+
+    @Override
+    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+
+        if (!hasGetParentLayoutMode) {
+            compatibleWithLayoutManager(parent);
+            hasGetParentLayoutMode = true;
+        }
+        int viewPosition = parent.getLayoutManager().getPosition(view);
+
+        if (mMode == RVItemDecorationConst.MODE_HORIZONTAL) {
+
+            if (!isIgnoreType(parent.getAdapter().getItemViewType(viewPosition))) {
+                if (!(!mLastLineVisible &&
+                        viewPosition == parent.getAdapter().getItemCount() - 1)) {
+                    outRect.set(0, 0, 0, mCurrentThickness);
+                }
+
+                if (mFirstLineVisible && viewPosition == 0) {
+                    outRect.set(0, mCurrentThickness, 0, mCurrentThickness);
+                }
+            } else {
+                outRect.set(0, 0, 0, 0);
+            }
+
+        } else if (mMode == RVItemDecorationConst.MODE_VERTICAL) {
+            if (!isIgnoreType(parent.getAdapter().getItemViewType(viewPosition))) {
+                if (!(!mLastLineVisible &&
+                        viewPosition == parent.getAdapter().getItemCount() - 1)) {
+                    outRect.set(0, 0, mCurrentThickness, 0);
+                }
+                if (mFirstLineVisible && viewPosition == 0) {
+                    outRect.set(mCurrentThickness, 0, mCurrentThickness, 0);
+                }
+
+            } else {
+                outRect.set(0, 0, 0, 0);
+            }
+        }
+
+    }
+
+    private boolean isPureLine() {
+        if (mDashGap == 0 && mDashWidth == 0) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * draw horizontal decoration
+     *
+     * @param c
+     * @param parent
+     */
+    private void drawHorizontal(Canvas c, RecyclerView parent) {
+
+        int childrenCount = parent.getChildCount();
+
+        if (parent.getClipToPadding()) {
+            int top = parent.getPaddingTop();
+            int bottom = parent.getHeight() - parent.getPaddingBottom();
+            c.clipRect(parent.getPaddingLeft(), top,
+                    parent.getWidth() - parent.getPaddingRight(), bottom);
+        }
+
+        if (mDrawableRid != 0) {
+
+            if (mFirstLineVisible) {
+                View childView = parent.getChildAt(0);
+                int myY = childView.getTop();
+
+                if (!isIgnoreType(parent.getAdapter().getItemViewType(
+                        parent.getLayoutManager().getPosition(childView)))) {
+
+                    if (hasNinePatch) {
+                        Rect rect = new Rect(mPaddingStart + parent.getPaddingLeft(), myY - mCurrentThickness
+                                , parent.getWidth() - parent.getPaddingRight() - mPaddingEnd, myY);
+                        mNinePatch.draw(c, rect);
+                    } else {
+                        c.drawBitmap(mBmp, mPaddingStart + parent.getPaddingLeft(), myY - mCurrentThickness, mPaint);
+                    }
+                }
+            }
+
+
+            for (int i = 0; i < childrenCount; i++) {
+                if (!mLastLineVisible && i == childrenCount - 1) {
+                    break;
+                }
+                View childView = parent.getChildAt(i);
+
+                if (!isIgnoreType(parent.getAdapter().getItemViewType(
+                        parent.getLayoutManager().getPosition(childView)))) {
+
+                    int myY = childView.getBottom();
+
+                    if (hasNinePatch) {
+                        Rect rect = new Rect(mPaddingStart + parent.getPaddingLeft(), myY
+                                , parent.getWidth() - parent.getPaddingRight() - mPaddingEnd, myY + mCurrentThickness);
+                        mNinePatch.draw(c, rect);
+                    } else {
+                        c.drawBitmap(mBmp, mPaddingStart + parent.getPaddingLeft(), myY, mPaint);
+                    }
+                }
+
+            }
+
+        } else {
+
+            boolean isPureLine = isPureLine();
+            if (!isPureLine) {
+                PathEffect effects = new DashPathEffect(new float[]{0, 0, mDashWidth, mCurrentThickness}, mDashGap);
+                mPaint.setPathEffect(effects);
+            }
+
+            if (mFirstLineVisible) {
+
+                View childView = parent.getChildAt(0);
+
+                if (!isIgnoreType(parent.getAdapter().getItemViewType(
+                        parent.getLayoutManager().getPosition(childView)))) {
+
+                    int myY = childView.getTop() - mCurrentThickness / 2;
+
+                    Path path = new Path();
+                    path.moveTo(mPaddingStart + parent.getPaddingLeft(), myY);
+                    path.lineTo(parent.getWidth() - mPaddingEnd - parent.getPaddingRight(), myY);
+                    c.drawPath(path, mPaint);
+                }
+            }
+
+            for (int i = 0; i < childrenCount; i++) {
+                if (!mLastLineVisible && i == childrenCount - 1) {
+                    break;
+                }
+                View childView = parent.getChildAt(i);
+
+                if (!isIgnoreType(parent.getAdapter().getItemViewType(
+                        parent.getLayoutManager().getPosition(childView)))) {
+
+                    int myY = childView.getBottom() + mCurrentThickness / 2;
+                    Path path = new Path();
+                    path.moveTo(mPaddingStart + parent.getPaddingLeft(), myY);
+                    path.lineTo(parent.getWidth() - mPaddingEnd - parent.getPaddingRight(), myY);
+                    c.drawPath(path, mPaint);
+
+                }
+
+            }
+
+        }
+    }
+
+    /**
+     * draw vertival decoration
+     *
+     * @param c
+     * @param parent
+     */
+    private void drawVertical(Canvas c, RecyclerView parent) {
+        int childrenCount = parent.getChildCount();
+
+        if (parent.getClipToPadding()) {
+            int left = parent.getPaddingLeft();
+            int right = parent.getWidth() - parent.getPaddingRight();
+            c.clipRect(left, parent.getPaddingTop(), right,
+                    parent.getHeight() - parent.getPaddingBottom());
+        }
+
+        if (mDrawableRid != 0) {
+
+            if (mFirstLineVisible) {
+                View childView = parent.getChildAt(0);
+
+                if (!isIgnoreType(parent.getAdapter().getItemViewType(
+                        parent.getLayoutManager().getPosition(childView)))) {
+                    int myX = childView.getLeft();
+                    if (hasNinePatch) {
+                        Rect rect = new Rect(myX - mCurrentThickness, mPaddingStart + parent.getPaddingLeft()
+                                , myX, parent.getHeight() - mPaddingEnd - parent.getPaddingRight());
+                        mNinePatch.draw(c, rect);
+                    } else {
+                        c.drawBitmap(mBmp, myX - mCurrentThickness, mPaddingStart + parent.getPaddingLeft(), mPaint);
+                    }
+                }
+            }
+            for (int i = 0; i < childrenCount; i++) {
+                if (!mLastLineVisible && i == childrenCount - 1) {
+                    break;
+                }
+                View childView = parent.getChildAt(i);
+
+                if (!isIgnoreType(parent.getAdapter().getItemViewType(
+                        parent.getLayoutManager().getPosition(childView)))) {
+
+                    int myX = childView.getRight();
+                    if (hasNinePatch) {
+                        Rect rect = new Rect(myX, mPaddingStart + parent.getPaddingLeft(), myX + mCurrentThickness
+                                , parent.getHeight() - mPaddingEnd - parent.getPaddingRight());
+                        mNinePatch.draw(c, rect);
+                    } else {
+                        c.drawBitmap(mBmp, myX, mPaddingStart + parent.getPaddingLeft(), mPaint);
+                    }
+                }
+            }
+
+        } else {
+
+            boolean isPureLine = isPureLine();
+            if (!isPureLine) {
+                PathEffect effects = new DashPathEffect(new float[]{0, 0, mDashWidth, mCurrentThickness}, mDashGap);
+                mPaint.setPathEffect(effects);
+            }
+
+            if (mFirstLineVisible) {
+                View childView = parent.getChildAt(0);
+                if (!isIgnoreType(parent.getAdapter().getItemViewType(
+                        parent.getLayoutManager().getPosition(childView)))) {
+                    int myX = childView.getLeft() - mCurrentThickness / 2;
+                    Path path = new Path();
+                    path.moveTo(myX, mPaddingStart + parent.getPaddingLeft());
+                    path.lineTo(myX, parent.getHeight() - mPaddingEnd - parent.getPaddingRight());
+                    c.drawPath(path, mPaint);
+                }
+            }
+
+            for (int i = 0; i < childrenCount; i++) {
+                if (!mLastLineVisible && i == childrenCount - 1) {
+                    break;
+                }
+                View childView = parent.getChildAt(i);
+
+                if (!isIgnoreType(parent.getAdapter().getItemViewType(
+                        parent.getLayoutManager().getPosition(childView)))) {
+
+                    int myX = childView.getRight() + mCurrentThickness / 2;
+                    Path path = new Path();
+                    path.moveTo(myX, mPaddingStart + parent.getPaddingLeft());
+                    path.lineTo(myX, parent.getHeight() - mPaddingEnd - parent.getPaddingRight());
+                    c.drawPath(path, mPaint);
+                }
+
+            }
+        }
+    }
+
+    /**
+     * check if is one of the first columns
+     *
+     * @param position
+     * @param columnSize
+     * @return
+     */
+    private boolean isFirstGridColumn(int position, int columnSize) {
+
+        return position % columnSize == 0;
+    }
+
+    /**
+     * check if is one of the last columns
+     *
+     * @param position
+     * @param columnSize
+     * @return
+     */
+    private boolean isLastGridColumn(int position, int itemSize, int columnSize) {
+        boolean isLast = false;
+        if ((position + 1) % columnSize == 0) {
+            isLast = true;
+        }
+        return isLast;
+    }
+
+    /**
+     * check if is the first row of th grid
+     *
+     * @param position
+     * @param columnSize
+     * @return
+     */
+    private boolean isFirstGridRow(int position, int columnSize) {
+        return position < columnSize;
+    }
+
+    /**
+     * check if is the last row of the grid
+     *
+     * @param position
+     * @param itemSize
+     * @param columnSize
+     * @return
+     */
+    private boolean isLastGridRow(int position, int itemSize, int columnSize) {
+        int temp = itemSize % columnSize;
+        if (temp == 0 && position >= itemSize - columnSize) {
+            return true;
+        } else if (position >= itemSize / columnSize * columnSize) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * check if is the last second row of the grid when the itemSize cannot be divided by columnSize
+     *
+     * @param position
+     * @param itemSize
+     * @param columnSize
+     * @return
+     */
+    private boolean isLastSecondGridRowNotDivided(int position, int itemSize, int columnSize) {
+        int temp = itemSize % columnSize;
+        if (temp != 0 && itemSize - 1 - temp == position) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * compatible with recyclerview layoutmanager
+     *
+     * @param parent
+     */
+    private void compatibleWithLayoutManager(RecyclerView parent) {
+
+        if (parent.getLayoutManager() != null) {
+            if (parent.getLayoutManager() instanceof LinearLayoutManager) {
+                if (((LinearLayoutManager) parent.getLayoutManager()).getOrientation() == RecyclerView.HORIZONTAL) {
+                    mMode = RVItemDecorationConst.MODE_VERTICAL;
+                } else {
+                    mMode = RVItemDecorationConst.MODE_HORIZONTAL;
+                }
+            } else {
+                try {
+                    throw new IllegalAccessException("RVLinearItemDecoration only support LinearLayoutManager");
+                } catch (IllegalAccessException e) {
+                    e.printStackTrace();
+                    return;
+                }
+            }
+
+        } else {
+            try {
+                throw new IllegalAccessException("RVLinearItemDecoration only support LinearLayoutManager");
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+                return;
+            }
+        }
+
+        initPaint(mContext);
+
+    }
+
+    /**
+     * check current item is ignore type
+     *
+     * @return
+     */
+    private boolean isIgnoreType(int viewType) {
+
+        if (mIgnoreTypes == null || mIgnoreTypes.isEmpty()) {
+            return false;
+        }
+
+        if (mIgnoreTypes.containsKey(viewType)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public static class Builder {
+
+        private Param params;
+        private Context context;
+
+        public Builder(Context context) {
+
+            params = new Param();
+            this.context = context;
+
+        }
+
+        public RVLinearItemDecoration create() {
+            RVLinearItemDecoration RVLinearItemDecoration = new RVLinearItemDecoration();
+            RVLinearItemDecoration.setParams(context, params);
+            return RVLinearItemDecoration;
+        }
+
+        public Builder drawableID(@DrawableRes int drawableID) {
+            params.drawableRid = drawableID;
+            return this;
+        }
+
+        public Builder color(@ColorInt int color) {
+            params.color = color;
+            return this;
+        }
+
+        public Builder color(String color) {
+            if (RVItemDecorationUtil.isColorString(color)) {
+                params.color = Color.parseColor(color);
+            }
+            return this;
+        }
+
+        public Builder thickness(int thickness) {
+            if (thickness % 2 != 0) {
+                thickness += 1;
+            }
+            if (thickness <= 2) {
+                thickness = 2;
+            }
+            params.thickness = thickness;
+            return this;
+        }
+
+        public Builder dashWidth(int dashWidth) {
+            if (dashWidth < 0) {
+                dashWidth = 0;
+            }
+            params.dashWidth = dashWidth;
+            return this;
+        }
+
+        public Builder dashGap(int dashGap) {
+            if (dashGap < 0) {
+                dashGap = 0;
+            }
+            params.dashGap = dashGap;
+            return this;
+        }
+
+        public Builder lastLineVisible(boolean visible) {
+            params.lastLineVisible = visible;
+            return this;
+        }
+
+        public Builder firstLineVisible(boolean visible) {
+            params.firstLineVisible = visible;
+            return this;
+        }
+
+        public Builder paddingStart(int padding) {
+            if (padding < 0) {
+                padding = 0;
+            }
+            params.paddingStart = padding;
+            return this;
+        }
+
+        public Builder paddingEnd(int padding) {
+            if (padding < 0) {
+                padding = 0;
+            }
+            params.paddingEnd = padding;
+            return this;
+        }
+
+        public Builder ignoreTypes(int[] ignoreTypes) {
+            params.ignoreTypes = ignoreTypes;
+            return this;
+        }
+
+    }
+
+    private static class Param {
+
+        public int drawableRid = 0;
+        public int color = Color.parseColor(DEFAULT_COLOR);
+        public int thickness;
+        public int dashWidth = 0;
+        public int dashGap = 0;
+        public boolean lastLineVisible;
+        public boolean firstLineVisible;
+        public int paddingStart;
+        public int paddingEnd;
+        public int[] ignoreTypes;
+    }
+
+}

+ 57 - 0
app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVLinearSpaceItemDecoration.java

@@ -0,0 +1,57 @@
+package com.sunwin.visitorapp.view.itemDecoration;
+
+import android.content.Context;
+import android.graphics.Color;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * RecycleView item decoration for only empty spacing
+ * Created by Eminem Lo on 2017/9/7.
+ * email: arjinmc@hotmail.com
+ */
+
+public class RVLinearSpaceItemDecoration extends RecyclerView.ItemDecoration {
+
+    public RVLinearSpaceItemDecoration() {
+        throw new RuntimeException("Use Builder to create!");
+    }
+
+    public static class Builder {
+
+        private Context context;
+        private Param param;
+
+        public Builder(Context context) {
+            this.context = context;
+            param = new Param();
+        }
+
+        public Builder margin(int margin) {
+            param.margin = margin;
+            return this;
+        }
+
+        public Builder ignoreTypes(int[] ignoreTypes) {
+            param.ignoreTypes = ignoreTypes;
+            return this;
+        }
+
+        public RVLinearItemDecoration create() {
+
+            return new RVLinearItemDecoration.Builder(context)
+                    .thickness(param.margin)
+                    .color(Color.TRANSPARENT)
+                    .ignoreTypes(param.ignoreTypes)
+                    .create();
+        }
+
+    }
+
+    public static class Param {
+
+        public int margin;
+        public int[] ignoreTypes;
+
+    }
+}

+ 49 - 0
app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVPaint.java

@@ -0,0 +1,49 @@
+package com.sunwin.visitorapp.view.itemDecoration;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.DashPathEffect;
+import android.graphics.NinePatch;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PathEffect;
+import android.graphics.Rect;
+
+/**
+ * Draw line/image for RecyclerViewItemDecoration
+ * Created by Eminem Lo on 2018/8/18.
+ * email: arjinmc@hotmail.com
+ */
+public final class RVPaint {
+
+    public static void drawNinePatch(Canvas canvas, NinePatch ninePatch, Rect rect) {
+        ninePatch.draw(canvas, rect);
+    }
+
+    public static void drawNinePatch(Canvas canvas, NinePatch ninePatch, int left, int top, int right, int bottom) {
+        ninePatch.draw(canvas, new Rect(left, top, right, bottom));
+    }
+
+    public static void drawBitmap(Canvas canvas, Bitmap bitmap, Paint paint, int left, int top) {
+        canvas.drawBitmap(bitmap, left, top, paint);
+    }
+
+    public static void drawLine(Canvas canvas, Paint paint, int startX, int startY, int stopX, int stopY) {
+        drawDashLine(canvas, paint, 0, 0, startX, startY, stopX, stopY);
+    }
+
+    public static void drawDashLine(Canvas canvas, Paint paint, int dashWidth, int dashGap
+            , int startX, int startY, int stopX, int stopY) {
+        if (dashWidth == 0 && dashGap == 0) {
+            PathEffect effects = new DashPathEffect(new float[]{0, 0, dashWidth, paint.getStrokeWidth()}, dashGap);
+            paint.setPathEffect(effects);
+        }
+        canvas.drawLine(startX, startY, stopX, stopY, paint);
+
+    }
+
+    public static void drawPath(Canvas canvas, Paint paint, Path path) {
+        canvas.drawPath(path, paint);
+    }
+
+}

+ 339 - 0
app/src/main/java/com/sunwin/visitorapp/view/itemDecoration/RVStickyHeadItemDecoration.java

@@ -0,0 +1,339 @@
+package com.sunwin.visitorapp.view.itemDecoration;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Sticky Head for RecyclerView
+ * Created by Eminem Lo on 2017/9/12.
+ * email: arjinmc@hotmail.com
+ * <p>
+ * <p> Attention!</p>
+ * RVStickyHeadItemDecoration
+ * only support of the RecyclerView with LinearLayoutManager.VERTICAL
+ */
+
+public class RVStickyHeadItemDecoration extends RecyclerView.ItemDecoration {
+
+    private View mStickyView;
+    private RecyclerView mParent;
+    /**
+     * mark the group view's width and height
+     */
+    private int mStickyViewHeight;
+    private int mStickyViewMarginTop;
+
+    /**
+     * default group view type is zero.
+     */
+    private int mGroupViewType = 0;
+
+    /**
+     * position list
+     */
+    private List<Integer> mStickyPositionList = new ArrayList<>();
+
+    /**
+     * bind position of sticky view
+     */
+    private int mBindStickyViewPosition = -1;
+    /**
+     * current StickyView Holder
+     */
+    private RecyclerView.ViewHolder mCurrentStickyViewHolder;
+
+    public void setParam(Param param) {
+        mGroupViewType = param.groupViewType;
+    }
+
+    @Override
+    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
+        super.onDrawOver(c, parent, state);
+
+        if (parent ==null || parent.getAdapter() == null || parent.getAdapter().getItemCount() == 0
+                || parent.getLayoutManager() == null) {
+            return;
+        }
+
+        if (parent.getLayoutManager() instanceof LinearLayoutManager) {
+            LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
+
+            boolean findStickView = false;
+
+            int itemCount = parent.getChildCount();
+            for (int i = 0; i < itemCount; i++) {
+                View view = parent.getChildAt(i);
+
+                if (isGroupViewType(parent.getChildAdapterPosition(view))) {
+                    findStickView = true;
+                    if (mCurrentStickyViewHolder == null) {
+                        mCurrentStickyViewHolder = parent.getAdapter().onCreateViewHolder(parent, mGroupViewType);
+                        mStickyView = mCurrentStickyViewHolder.itemView;
+                    }
+                    if (view.getTop() <= 0) {
+                        bindDataForStickyView(layoutManager.findFirstVisibleItemPosition());
+                    } else {
+                        if (mStickyPositionList.size() > 0) {
+                            if (mStickyPositionList.size() == 1) {
+                                bindDataForStickyView(mStickyPositionList.get(0));
+                            } else {
+                                int currentPosition = layoutManager.findFirstVisibleItemPosition() + i;
+                                int indexOfCurrentPosition = mStickyPositionList.lastIndexOf(currentPosition);
+                                bindDataForStickyView(mStickyPositionList.get(indexOfCurrentPosition - 1));
+                            }
+                        }
+                    }
+
+                    if (view.getTop() > 0 && view.getTop() <= mStickyViewHeight) {
+                        mStickyViewMarginTop = mStickyViewHeight - view.getTop();
+                    } else {
+                        mStickyViewMarginTop = 0;
+
+                        View nextStickyView = getNextStickyView();
+                        if (nextStickyView != null && nextStickyView.getTop() <= mStickyViewHeight) {
+                            mStickyViewMarginTop = mStickyViewHeight - nextStickyView.getTop();
+                        }
+
+                    }
+
+                    drawStickyView(c);
+                    break;
+                }
+            }
+
+            if (!findStickView) {
+                mStickyViewMarginTop = 0;
+                if (layoutManager.findFirstVisibleItemPosition() + parent.getChildCount() == parent.getAdapter().getItemCount()
+                        && mStickyPositionList.size() > 0) {
+                    bindDataForStickyView(mStickyPositionList.get(mStickyPositionList.size() - 1));
+                }
+                drawStickyView(c);
+            }
+        } else {
+            try {
+                throw new IllegalAccessException("Only support RecyclerView LinearLayoutManager.VERTICAL");
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @Override
+    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+        super.getItemOffsets(outRect, view, parent, state);
+
+        if (mParent == null) {
+            if (parent.getLayoutManager() instanceof LinearLayoutManager) {
+                if (((LinearLayoutManager) parent.getLayoutManager()).getOrientation()
+                        != RecyclerView.VERTICAL) {
+                    throw new IllegalArgumentException("Only support LinearLayoutManager.VERTICAL");
+                }
+            }
+            mParent = parent;
+            initListener();
+        }
+    }
+
+    /**
+     * add listener to RecyclerView
+     */
+    private void initListener() {
+
+        // update sticky position list if data of RecyclerView has changed
+        mParent.getAdapter().registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
+            @Override
+            public void onChanged() {
+                super.onChanged();
+                initStickyPositionList();
+            }
+
+            @Override
+            public void onItemRangeChanged(int positionStart, int itemCount) {
+                super.onItemRangeChanged(positionStart, itemCount);
+                initStickyPositionList();
+            }
+
+            @Override
+            public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
+                super.onItemRangeChanged(positionStart, itemCount, payload);
+                initStickyPositionList();
+            }
+
+            @Override
+            public void onItemRangeInserted(int positionStart, int itemCount) {
+                super.onItemRangeInserted(positionStart, itemCount);
+                initStickyPositionList();
+            }
+
+            @Override
+            public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
+                super.onItemRangeMoved(fromPosition, toPosition, itemCount);
+                initStickyPositionList();
+            }
+
+            @Override
+            public void onItemRangeRemoved(int positionStart, int itemCount) {
+                super.onItemRangeRemoved(positionStart, itemCount);
+                initStickyPositionList();
+            }
+        });
+        initStickyPositionList();
+    }
+
+    private void initStickyPositionList() {
+        if (mParent == null || mParent.getAdapter() == null) {
+            return;
+        }
+        if (mStickyPositionList == null) {
+            mStickyPositionList = new ArrayList<>();
+        } else {
+            mStickyPositionList.clear();
+        }
+        int itemCount = mParent.getAdapter().getItemCount();
+        if (itemCount > 0) {
+            for (int i = 0; i < itemCount; i++) {
+                if (isGroupViewType(i)) {
+                    mStickyPositionList.add(i);
+                }
+            }
+            View lGroupView = mParent.getChildAt(mStickyPositionList.get(0));
+            if (lGroupView != null) {
+                lGroupView.measure(0, 0);
+                mStickyViewHeight = lGroupView.getMeasuredHeight();
+            }
+        }
+    }
+
+    /**
+     * draw sticky view
+     *
+     * @param canvas
+     */
+    private void drawStickyView(Canvas canvas) {
+        if (mStickyView == null) {
+            return;
+        }
+        int saveCount = canvas.save();
+        canvas.translate(0, -mStickyViewMarginTop);
+        mStickyView.draw(canvas);
+        canvas.restoreToCount(saveCount);
+    }
+
+    /**
+     * bind data for sticky view
+     *
+     * @param position
+     */
+    private void bindDataForStickyView(int position) {
+        if (mBindStickyViewPosition == position || mCurrentStickyViewHolder == null) {
+            return;
+        }
+        mBindStickyViewPosition = position;
+        mParent.getAdapter().onBindViewHolder(mCurrentStickyViewHolder, mBindStickyViewPosition);
+        mStickyView = mCurrentStickyViewHolder.itemView;
+        measureStickyView();
+        mStickyViewHeight = mCurrentStickyViewHolder.itemView.getBottom() - mCurrentStickyViewHolder.itemView.getTop();
+    }
+
+    /**
+     * get the next sticky view
+     *
+     * @return view
+     */
+    private View getNextStickyView() {
+        if (mParent == null) {
+            return null;
+        }
+        int num = 0;
+        View nextStickyView = null;
+        int size = mParent.getChildCount();
+        for (int i = 0; i < size; i++) {
+            View view = mParent.getChildAt(i);
+            if (isGroupViewType(mParent.getChildAdapterPosition(view))) {
+                nextStickyView = view;
+                num++;
+            }
+            if (num == 2) {
+                break;
+            }
+        }
+        return num >= 2 ? nextStickyView : null;
+    }
+
+    /**
+     * measure sticky view
+     */
+    private void measureStickyView() {
+        if (mParent == null || mStickyView == null || !mStickyView.isLayoutRequested()) {
+            return;
+        }
+
+        int parentWidth = mParent.getMeasuredWidth();
+        int widthSpec = View.MeasureSpec.makeMeasureSpec(parentWidth, View.MeasureSpec.EXACTLY);
+        int heightSpec;
+
+        ViewGroup.LayoutParams layoutParams = mStickyView.getLayoutParams();
+        if (layoutParams != null && layoutParams.height > 0) {
+            heightSpec = View.MeasureSpec.makeMeasureSpec(layoutParams.height, View.MeasureSpec.EXACTLY);
+        } else {
+            heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+        }
+
+        mStickyView.measure(widthSpec, heightSpec);
+        mStickyView.layout(0, 0, mStickyView.getMeasuredWidth(), mStickyView.getMeasuredHeight());
+    }
+
+    /**
+     * chech the view of target position is GroupView type
+     *
+     * @param position
+     * @return
+     */
+    private boolean isGroupViewType(int position) {
+        if (mParent == null || mParent.getAdapter() == null) {
+            return false;
+        }
+        if (mParent.getAdapter().getItemViewType(position) == mGroupViewType) {
+
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Builder to create Sticky Head
+     */
+    public static class Builder {
+
+        private Param param;
+
+        public Builder() {
+            param = new Param();
+        }
+
+        public Builder groupViewType(int groupViewType) {
+            param.groupViewType = groupViewType;
+            return this;
+        }
+
+        public RVStickyHeadItemDecoration create() {
+            RVStickyHeadItemDecoration RVStickyHeadItemDecoration = new RVStickyHeadItemDecoration();
+            RVStickyHeadItemDecoration.setParam(param);
+            return RVStickyHeadItemDecoration;
+        }
+    }
+
+    private static class Param {
+
+        public int groupViewType;
+    }
+}

+ 15 - 0
app/src/main/res/drawable/border_gary.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <!--设置圆角-->
+    <!--  按顺序分别对应:左下角的角度、右下角的角度、左上角的角度、右上角的角度-->
+    <corners
+        android:bottomLeftRadius="@dimen/Normal_Card_Radius"
+        android:bottomRightRadius="@dimen/Normal_Card_Radius"
+        android:topLeftRadius="@dimen/Normal_Card_Radius"
+        android:topRightRadius="@dimen/Normal_Card_Radius" />
+    <!--  设置边框大小与背景色-->
+    <stroke
+        android:width="2dp"
+        android:color="@color/ColorGray" />
+
+</shape>

+ 27 - 0
app/src/main/res/drawable/folder_bg.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item
+        android:left="3dp"
+        android:top="3dp">
+        <shape>
+            <stroke
+                android:width="0.5dp"
+                android:color="#757878" />
+            <solid android:color="@android:color/white" />
+        </shape>
+    </item>
+
+    <item
+        android:bottom="1.5dp"
+        android:right="1.5dp"
+        android:top="1.5dp"
+        android:left="1.5dp">
+        <shape>
+            <stroke
+                android:width="0.5dp"
+                android:color="#757878" />
+            <solid android:color="@android:color/white" />
+        </shape>
+    </item>
+</layer-list>

BIN
app/src/main/res/drawable/icon_image_select.png


+ 38 - 0
app/src/main/res/drawable/image_temp.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+
+    android:width="15dp"
+    android:height="15dp"
+    android:viewportWidth="1024"
+    android:viewportHeight="1024"
+
+    >
+    <group android:rotation="0" android:pivotX="512" android:pivotY="512"
+        >
+        <path
+            android:fillColor="#3D3D3D"
+            android:pathData=
+                "M512 0C229.229714 0 0 229.229714 0 512s229.229714 512 512 512 512-229.229714 512-512S794.770286 0 512 0z m274.285714 656.618057V704.365714a21.942857 21.942857 0 0 1-21.942857 21.942857H259.657143a21.942857 21.942857 0 0 1-21.942857-21.942857V319.634286a21.942857 21.942857 0 0 1 21.942857-21.942857h504.685714a21.942857 21.942857 0 0 1 21.942857 21.942857z"
+            />
+        <path
+            android:fillColor="#6ACC9B"
+            android:pathData=
+                "M764.342857 726.308571H259.657143a21.942857 21.942857 0 0 1-21.942857-21.942857 21.942857 21.942857 0 0 0 21.942857 21.942857h504.685714a21.942857 21.942857 0 0 0 21.942857-21.942857 21.942857 21.942857 0 0 1-21.942857 21.942857zM764.342857 297.691429H259.657143a21.942857 21.942857 0 0 0-21.942857 21.942857v275.616914c24.693029-69.756343 91.223771-119.727543 169.435428-119.727543 74.920229 0 139.117714 45.860571 166.0928 111.030857A137.618286 137.618286 0 0 1 786.285714 656.618057V319.634286a21.942857 21.942857 0 0 0-21.942857-21.942857zM661.284571 456.023771a48.493714 48.493714 0 1 1 48.574172-48.493714 48.530286 48.530286 0 0 1-48.574172 48.493714z"
+            />
+        <path
+            android:fillColor="#FFFFFF"
+            android:pathData=
+                "M612.7104 407.530057a48.574171 48.493714 0 1 0 97.148343 0 48.574171 48.493714 0 1 0-97.148343 0Z"
+            />
+        <path
+            android:fillColor="#73E5A1"
+            android:pathData=
+                "M654.826057 559.754971a136.981943 136.981943 0 0 0-81.583543 26.799543 180.136229 180.136229 0 0 1-1.002057 139.702857H764.342857a21.942857 21.942857 0 0 0 21.942857-21.942857v-47.696457a137.647543 137.647543 0 0 0-131.459657-96.863086z"
+            />
+        <path
+            android:fillColor="#95EFBA"
+            android:pathData=
+                "M259.657143 726.308571h312.583314a180.136229 180.136229 0 0 0 1.002057-139.702857c-26.975086-65.170286-91.172571-111.030857-166.0928-111.030857-78.211657 0-144.7424 49.9712-169.435428 119.727543V704.365714a21.942857 21.942857 0 0 0 21.942857 21.942857z"
+            />
+    </group>
+</vector>

+ 2 - 4
app/src/main/res/layout/activity_home.xml

@@ -33,12 +33,11 @@
         android:id="@+id/ImageStatus"
         android:layout_width="0dp"
         android:layout_height="0dp"
-        android:layout_marginEnd="10dp"
         android:padding="10dp"
         android:scaleType="fitCenter"
         android:src="@drawable/ui_vector_status"
         app:layout_constraintBottom_toBottomOf="@id/TextTitle"
-        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintDimensionRatio="1.2:1"
         app:layout_constraintEnd_toStartOf="@id/ImageSetting"
         app:layout_constraintTop_toTopOf="@id/TextTitle" />
 
@@ -46,12 +45,11 @@
         android:id="@+id/ImageSetting"
         android:layout_width="0dp"
         android:layout_height="0dp"
-        android:layout_marginEnd="20dp"
         android:padding="10dp"
         android:scaleType="fitCenter"
         android:src="@drawable/ui_vector_setting"
         app:layout_constraintBottom_toBottomOf="@id/TextTitle"
-        app:layout_constraintDimensionRatio="1:1"
+        app:layout_constraintDimensionRatio="1.2:1"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="@id/TextTitle" />
 

+ 70 - 0
app/src/main/res/layout/adapter_folder.xml

@@ -0,0 +1,70 @@
+<?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">
+
+    <FrameLayout
+        android:id="@+id/fl_folder"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@drawable/folder_bg"
+        android:layout_margin="10dp"
+        android:paddingRight="3dp"
+        android:paddingBottom="3dp">
+
+        <ImageView
+            android:id="@+id/iv_image"
+            android:layout_width="80dp"
+            android:layout_height="80dp"
+            android:background="@android:color/white"
+            android:scaleType="centerCrop" />
+
+    </FrameLayout>
+
+    <ImageView
+        android:id="@+id/iv_select"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:layout_marginRight="15dp"
+        android:src="@drawable/icon_image_select" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_marginLeft="5dp"
+        android:layout_marginRight="5dp"
+        android:layout_toLeftOf="@+id/iv_select"
+        android:layout_toRightOf="@+id/fl_folder"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/tv_folder_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textColor="@android:color/black"
+            android:textSize="16sp" />
+
+        <TextView
+            android:id="@+id/tv_folder_size"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="3dp"
+            android:singleLine="true"
+            android:textColor="@android:color/darker_gray"
+            android:textSize="14sp" />
+
+    </LinearLayout>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1px"
+        android:layout_below="@+id/fl_folder"
+        android:layout_marginLeft="10dp"
+        android:layout_marginRight="10dp"
+        android:background="@android:color/darker_gray" />
+
+</RelativeLayout>

+ 76 - 0
app/src/main/res/layout/dialog_blacklist_add.xml

@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="500dp"
+    android:focusableInTouchMode="true"
+    android:focusable="true"
+    android:padding="20dp"
+    android:layout_height="wrap_content">
+
+    <LinearLayout
+        android:layout_marginVertical="10dp"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:gravity="center_vertical"
+        android:layout_height="wrap_content">
+        <TextView
+            style="@style/StyleTextContent"
+            android:layout_width="150dp"
+            android:text="人员姓名"
+            />
+        <EditText
+            android:id="@+id/EditName"
+            android:layout_weight="1"
+            android:layout_width="0dp"
+            style="@style/StyleInputContent"
+            android:layout_height="wrap_content"/>
+    </LinearLayout>
+    <LinearLayout
+        android:layout_marginVertical="10dp"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:gravity="center_vertical"
+        android:layout_height="wrap_content">
+        <TextView
+            style="@style/StyleTextContent"
+            android:layout_width="150dp"
+            android:text="身份证号"
+            />
+        <EditText
+            android:id="@+id/EditICCode"
+            android:layout_weight="1"
+            android:layout_width="0dp"
+            style="@style/StyleInputContent"
+            android:layout_height="wrap_content"/>
+    </LinearLayout>
+    <LinearLayout
+        android:layout_marginVertical="10dp"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:gravity="center_vertical"
+        android:layout_height="wrap_content">
+        <TextView
+            style="@style/StyleTextContent"
+            android:layout_width="150dp"
+            android:text="人员照片"
+            />
+        <ImageView
+            android:id="@+id/ImagePhoto"
+            android:layout_weight="1"
+            android:layout_width="0dp"
+            android:layout_height="100dp"
+            android:scaleType="fitCenter"
+            android:adjustViewBounds="true"
+            android:src="@mipmap/ic_launcher"
+            />
+    </LinearLayout>
+    <Button
+        android:id="@+id/ButtonSure"
+        android:layout_gravity="center_horizontal"
+        style="@style/ButtonPrimaryLarge"
+        android:layout_width="match_parent"
+        android:textAlignment="center"
+        android:layout_margin="10dp"
+        android:text="提交"
+        />
+</LinearLayout>

+ 3 - 3
app/src/main/res/layout/fr_blacklist.xml

@@ -4,19 +4,19 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:paddingHorizontal="20dp">
+    android:paddingHorizontal="10dp">
 
     <androidx.constraintlayout.widget.ConstraintLayout
         android:layout_marginVertical="10dp"
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
         <Button
+            android:id="@+id/ButtonAdd"
             style="@style/ButtonPrimary"
 
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintBottom_toBottomOf="parent"
-            android:textSize="@dimen/TextSizeContent"
             android:text="新增"
             />
         <LinearLayout
@@ -48,13 +48,13 @@
                 android:layout_height="wrap_content"/>
             <Button
                 style="@style/ButtonPrimary"
-                android:textSize="@dimen/TextSizeContent"
                 android:text="搜索"
                 />
         </LinearLayout>
     </androidx.constraintlayout.widget.ConstraintLayout>
 
     <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/RecyclerBlackList"
         android:layout_marginVertical="10dp"
         android:layout_width="match_parent"
         android:layout_height="0dp"

+ 2 - 2
app/src/main/res/layout/fr_log_manage.xml

@@ -11,7 +11,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
         <Button
-            style="@style/ButtonPrimary"
+            style="@style/ButtonPrimaryLarge"
 
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent"
@@ -51,7 +51,7 @@
                 style="@style/StyleInputContent"
                 android:layout_height="wrap_content"/>
             <Button
-                style="@style/ButtonPrimary"
+                style="@style/ButtonPrimaryLarge"
                 android:layout_width="wrap_content"
                 android:textAlignment="center"
                 android:textSize="@dimen/TextSizeContent"

+ 1 - 1
app/src/main/res/layout/fr_page_manage.xml

@@ -83,7 +83,7 @@
     <Button
         android:id="@+id/TextButtonSure"
         android:layout_gravity="center_horizontal"
-        style="@style/ButtonPrimary"
+        style="@style/ButtonPrimaryLarge"
 
         android:layout_marginHorizontal="100dp"
         android:layout_width="match_parent"

+ 2 - 2
app/src/main/res/layout/fr_role_manage.xml

@@ -11,7 +11,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
         <Button
-            style="@style/ButtonPrimary"
+            style="@style/ButtonPrimaryLarge"
 
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent"
@@ -40,7 +40,7 @@
                 style="@style/StyleInputContent"
                 android:layout_height="wrap_content"/>
             <Button
-                style="@style/ButtonPrimary"
+                style="@style/ButtonPrimaryLarge"
                 android:layout_width="wrap_content"
                 android:textAlignment="center"
                 android:textSize="@dimen/TextSizeContent"

+ 1 - 1
app/src/main/res/layout/fr_system_option.xml

@@ -242,7 +242,7 @@
     <Button
         android:id="@+id/TextButtonSure"
         android:layout_gravity="center_horizontal"
-        style="@style/ButtonPrimary"
+        style="@style/ButtonPrimaryLarge"
 
         android:layout_marginHorizontal="100dp"
         android:layout_width="match_parent"

+ 2 - 2
app/src/main/res/layout/fr_user_manage.xml

@@ -11,7 +11,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
         <Button
-            style="@style/ButtonPrimary"
+            style="@style/ButtonPrimaryLarge"
 
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent"
@@ -40,7 +40,7 @@
                 style="@style/StyleInputContent"
                 android:layout_height="wrap_content"/>
             <Button
-                style="@style/ButtonPrimary"
+                style="@style/ButtonPrimaryLarge"
                 android:layout_width="wrap_content"
                 android:textAlignment="center"
                 android:textSize="@dimen/TextSizeContent"

+ 43 - 0
app/src/main/res/layout/item_blacklist.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:background="@drawable/border_gary"
+    android:layout_margin="1dp"
+    android:padding="10dp">
+
+    <ImageView
+        android:id="@+id/ImageHead"
+        android:scaleType="fitCenter"
+        android:layout_width="120dp"
+        android:layout_height="80dp"
+        android:adjustViewBounds="true"
+        android:src="@drawable/image_temp"
+        />
+    <LinearLayout
+        android:layout_marginHorizontal="15dp"
+        android:orientation="vertical"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+        <TextView
+            android:id="@+id/TextName"
+            style="@style/StyleTextContent"/>
+        <TextView
+            android:id="@+id/TextICCode"
+            style="@style/StyleTextContent"/>
+    </LinearLayout>
+    <Button
+        android:id="@+id/ButtonEdit"
+        android:text="编辑"
+        android:layout_margin="10dp"
+        style="@style/ButtonPrimary"/>
+    <Button
+        android:id="@+id/ButtonDelete"
+        android:text="删除"
+        android:layout_margin="10dp"
+        style="@style/ButtonPrimary"/>
+</LinearLayout>

+ 4 - 1
app/src/main/res/values/style.xml

@@ -24,13 +24,16 @@
         <item name="android:layout_marginHorizontal">5dp</item>
         <item name="android:background">@drawable/card_input</item>
     </style>
-    <style name="ButtonPrimary" parent="StyleTextLarge">
+    <style name="ButtonPrimary" parent="StyleTextContent">
         <item name="android:background">@drawable/button_primary</item>
         <item name="android:textAlignment">center</item>
         <item name="android:paddingHorizontal">18dp</item>
         <item name="android:paddingVertical">8dp</item>
         <item name="android:textColor">@color/ColorWhite</item>
     </style>
+    <style name="ButtonPrimaryLarge" parent="ButtonPrimary">
+        <item name="android:textSize">@dimen/TextSizeLarge</item>
+    </style>
     <color name="NavBackgroundColor">#00FFFFFF</color>
     <dimen name="NavigationTitleTextSize">@dimen/TextSizeBig</dimen>
     <dimen name="CardInputRadius">4dp</dimen>

+ 1 - 0
build.gradle

@@ -17,6 +17,7 @@ allprojects {
         google()
         mavenCentral()
         jcenter() // Warning: this repository is going to shut down soon
+        maven { url 'https://www.jitpack.io' }
     }
 }