项目列表

main
huangwei 9 months ago
parent d51ff9e71d
commit 077a84239f
  1. 12
      app/src/main/AndroidManifest.xml
  2. 21
      app/src/main/java/com/project/survey/activity/approval/ApprovalActivity.kt
  3. 15
      app/src/main/java/com/project/survey/activity/login/LoginActivity.kt
  4. 45
      app/src/main/java/com/project/survey/activity/project/ProjectListActivity.kt
  5. 66
      app/src/main/java/com/project/survey/activity/project/ProjectListFragment.kt
  6. 32
      app/src/main/java/com/project/survey/adapter/ProjectListAdapter.java
  7. 87
      app/src/main/java/com/project/survey/adapter/base/BaseAdapter.java
  8. 14
      app/src/main/java/com/project/survey/adapter/base/IAdapterContextMenu.java
  9. 5
      app/src/main/java/com/project/survey/adapter/base/IItemClickListener.java
  10. 15
      app/src/main/java/com/project/survey/adapter/viewholder/ProjectListViewHolder.java
  11. 5
      app/src/main/java/com/project/survey/fragment/home/MeFragment.kt
  12. 4
      app/src/main/java/com/project/survey/model/ProjectBean.kt
  13. 383
      app/src/main/java/com/project/survey/widget/decoration/DefaultItemDecoration.java
  14. 10
      app/src/main/java/com/project/survey/widget/decoration/HorDividerDecoration.kt
  15. 323
      app/src/main/java/com/project/survey/widget/edittext/PasswordEditText.java
  16. 57
      app/src/main/java/com/project/survey/widget/edittext/PasswordView.java
  17. 351
      app/src/main/java/com/project/survey/widget/swiperecyclerview/AdapterWrapper.java
  18. 139
      app/src/main/java/com/project/survey/widget/swiperecyclerview/Controller.java
  19. 472
      app/src/main/java/com/project/survey/widget/swiperecyclerview/ExpandableAdapter.java
  20. 80
      app/src/main/java/com/project/survey/widget/swiperecyclerview/Horizontal.java
  21. 72
      app/src/main/java/com/project/survey/widget/swiperecyclerview/LeftHorizontal.java
  22. 30
      app/src/main/java/com/project/survey/widget/swiperecyclerview/OnItemClickListener.java
  23. 30
      app/src/main/java/com/project/survey/widget/swiperecyclerview/OnItemLongClickListener.java
  24. 28
      app/src/main/java/com/project/survey/widget/swiperecyclerview/OnItemMenuClickListener.java
  25. 72
      app/src/main/java/com/project/survey/widget/swiperecyclerview/RightHorizontal.java
  26. 106
      app/src/main/java/com/project/survey/widget/swiperecyclerview/SwipeMenu.java
  27. 48
      app/src/main/java/com/project/survey/widget/swiperecyclerview/SwipeMenuBridge.java
  28. 31
      app/src/main/java/com/project/survey/widget/swiperecyclerview/SwipeMenuCreator.java
  29. 166
      app/src/main/java/com/project/survey/widget/swiperecyclerview/SwipeMenuItem.java
  30. 531
      app/src/main/java/com/project/survey/widget/swiperecyclerview/SwipeMenuLayout.java
  31. 121
      app/src/main/java/com/project/survey/widget/swiperecyclerview/SwipeMenuView.java
  32. 864
      app/src/main/java/com/project/survey/widget/swiperecyclerview/SwipeRecyclerView.java
  33. 132
      app/src/main/java/com/project/survey/widget/swiperecyclerview/touch/DefaultItemTouchHelper.java
  34. 174
      app/src/main/java/com/project/survey/widget/swiperecyclerview/touch/ItemTouchHelperCallback.java
  35. 42
      app/src/main/java/com/project/survey/widget/swiperecyclerview/touch/OnItemMoveListener.java
  36. 56
      app/src/main/java/com/project/survey/widget/swiperecyclerview/touch/OnItemMovementListener.java
  37. 50
      app/src/main/java/com/project/survey/widget/swiperecyclerview/touch/OnItemStateChangedListener.java
  38. 74
      app/src/main/java/com/project/survey/widget/swiperecyclerview/widget/BorderItemDecoration.java
  39. 47
      app/src/main/java/com/project/survey/widget/swiperecyclerview/widget/ColorDrawer.java
  40. 380
      app/src/main/java/com/project/survey/widget/swiperecyclerview/widget/DefaultItemDecoration.java
  41. 116
      app/src/main/java/com/project/survey/widget/swiperecyclerview/widget/DefaultLoadMoreView.java
  42. 84
      app/src/main/java/com/project/survey/widget/swiperecyclerview/widget/Drawer.java
  43. 406
      app/src/main/java/com/project/survey/widget/swiperecyclerview/widget/StickyNestedScrollView.java
  44. 6
      app/src/main/res/drawable/bg_search.xml
  45. 8
      app/src/main/res/drawable/bg_search_shadow_2.xml
  46. 10
      app/src/main/res/drawable/ic_project_all.xml
  47. 13
      app/src/main/res/drawable/ic_project_list.xml
  48. 10
      app/src/main/res/drawable/ic_project_recently.xml
  49. 10
      app/src/main/res/drawable/ic_search.xml
  50. 6
      app/src/main/res/layout/activity_approval.xml
  51. 7
      app/src/main/res/layout/activity_login.xml
  52. 36
      app/src/main/res/layout/activity_project_list.xml
  53. 24
      app/src/main/res/layout/activity_set_new_pwd.xml
  54. 7
      app/src/main/res/layout/fragment_me.xml
  55. 10
      app/src/main/res/layout/fragment_project_list.xml
  56. 57
      app/src/main/res/layout/item_project_list.xml
  57. 42
      app/src/main/res/layout/item_search.xml
  58. 8
      app/src/main/res/layout/view_password.xml
  59. 39
      app/src/main/res/layout/x_recycler_view_item.xml
  60. 37
      app/src/main/res/layout/x_recycler_view_load_more.xml
  61. 81
      app/src/main/res/values/attr.xml
  62. 3
      app/src/main/res/values/color.xml
  63. 7
      app/src/main/res/values/strings.xml

@ -89,8 +89,16 @@
android:exported="true"
android:label=""
android:windowSoftInputMode="adjustResize|stateHidden" />
<activity
android:name=".activity.project.ProjectListActivity"
android:exported="true"
android:label="@string/project_list"
android:windowSoftInputMode="adjustResize|stateHidden" />
<activity
android:name=".activity.approval.ApprovalActivity"
android:exported="true"
android:label="@string/project_list"
android:windowSoftInputMode="adjustResize|stateHidden" />
</application>

@ -0,0 +1,21 @@
package com.project.survey.activity.approval
import com.project.survey.activity.base.BaseBindingActivity
import com.project.survey.databinding.ActivityApprovalBinding
/**
* 待处理已处理已发起
*/
class ApprovalActivity : BaseBindingActivity<ActivityApprovalBinding>() {
override fun getBinding(): ActivityApprovalBinding {
return ActivityApprovalBinding.inflate(layoutInflater)
}
override fun initView() {
TODO("Not yet implemented")
}
override fun initData() {
TODO("Not yet implemented")
}
}

@ -1,8 +1,6 @@
package com.project.survey.activity.login
import android.content.Intent
import android.text.method.HideReturnsTransformationMethod
import android.text.method.PasswordTransformationMethod
import android.view.View
import com.project.survey.R
import com.project.survey.activity.base.BaseBindingActivity
@ -18,16 +16,6 @@ class LoginActivity : BaseBindingActivity<ActivityLoginBinding>() {
}
override fun initView() {
mBinding.ilPwd.pwdShowHide.setOnClickListener {
mBinding.ilPwd.ivPwdShowHide.isSelected = !mBinding.ilPwd.ivPwdShowHide.isSelected
if (mBinding.ilPwd.ivPwdShowHide.isSelected) {
mBinding.ilPwd.etPwd.transformationMethod = HideReturnsTransformationMethod.getInstance()
} else {
mBinding.ilPwd.etPwd.transformationMethod = PasswordTransformationMethod.getInstance()
}
mBinding.ilPwd.etPwd.setSelection(mBinding.ilPwd.etPwd.text?.length ?: 0)
}
mBinding.tvShowOutLogin.setOnClickListener {
//切换到外部登录
isOutLogin = true
@ -87,7 +75,8 @@ class LoginActivity : BaseBindingActivity<ActivityLoginBinding>() {
}
mBinding.etAccount.text = null
mBinding.ilPwd.etPwd.text = null
mBinding.passwordView.clearEtPwdText()
// mBinding.ilPwd.etPwd.text = null
}
override fun initData() {

@ -0,0 +1,45 @@
package com.project.survey.activity.project
import androidx.fragment.app.Fragment
import com.bingce.ui.TabEntity
import com.flyco.tablayout.listener.CustomTabEntity
import com.project.survey.R
import com.project.survey.activity.base.BaseBindingActivity
import com.project.survey.databinding.ActivityProjectListBinding
class ProjectListActivity : BaseBindingActivity<ActivityProjectListBinding>() {
override fun getBinding(): ActivityProjectListBinding {
return ActivityProjectListBinding.inflate(layoutInflater)
}
override fun initView() {
initTabLayout()
}
private fun initTabLayout() {
val mTabEntities = ArrayList<CustomTabEntity>()
mTabEntities.add(
TabEntity(
"最近打开",
R.drawable.ic_project_recently,
R.drawable.ic_project_recently
)
)
mTabEntities.add(
TabEntity(
"全部",
R.drawable.ic_project_all,
R.drawable.ic_project_all
)
)
val fragmentList = ArrayList<Fragment>()
fragmentList.add(ProjectListFragment.newInstance(ProjectListFragment.TYPE_RECENTLY))
fragmentList.add(ProjectListFragment.newInstance(ProjectListFragment.TYPE_ALL))
mBinding.tabLayout.setTabData(mTabEntities, this, R.id.container, fragmentList)
}
override fun initData() {
}
}

@ -0,0 +1,66 @@
package com.project.survey.activity.project
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import com.project.survey.adapter.ProjectListAdapter
import com.project.survey.databinding.FragmentProjectListBinding
import com.project.survey.fragment.base.BaseFragmentBinding
import com.project.survey.model.ProjectBean
import com.project.survey.widget.decoration.HorDividerDecoration
class ProjectListFragment : BaseFragmentBinding<FragmentProjectListBinding>() {
private val adapter by lazy { ProjectListAdapter(requireContext()) }
companion object {
const val TYPE_RECENTLY = "1"
const val TYPE_ALL = "2"
const val KEY_TYPE = "KEY_TYPE"
fun newInstance(type: String): Fragment {
val fragment = ProjectListFragment()
fragment.arguments = bundleOf(
KEY_TYPE to type
)
return fragment
}
}
override fun getViewBinding(
inflater: LayoutInflater,
container: ViewGroup?
): FragmentProjectListBinding {
return FragmentProjectListBinding.inflate(inflater, container, false)
}
override fun initView() {
initAdapter()
}
override fun initData() {
val type = getArgumentString(KEY_TYPE)
val num = if (type == TYPE_RECENTLY) {
3
} else {
6
}
val dataList = mutableListOf<ProjectBean>()
for (i in 0 until num) {
dataList.add(ProjectBean())
}
adapter.refreshData(dataList)
}
private fun initAdapter() {
mBinding.recyclerView.addItemDecoration(HorDividerDecoration(requireContext()))
adapter.setOnItemClickListener {
}
mBinding.recyclerView.setAdapter(adapter)
}
}

@ -0,0 +1,32 @@
package com.project.survey.adapter;
import android.content.Context;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.project.survey.adapter.base.BaseAdapter;
import com.project.survey.adapter.viewholder.ProjectListViewHolder;
import com.project.survey.databinding.ItemProjectListBinding;
import com.project.survey.model.ProjectBean;
public class ProjectListAdapter extends BaseAdapter<ProjectListViewHolder, ProjectBean> {
public ProjectListAdapter(Context context) {
super(context);
}
@NonNull
@Override
public ProjectListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ProjectListViewHolder(ItemProjectListBinding.inflate(getInflater(), parent, false));
}
@Override
protected void onBindViewHolderDo(ProjectListViewHolder holder, int position) {
ProjectBean bean = mDataList.get(position);
}
}

@ -0,0 +1,87 @@
package com.project.survey.adapter.base;
import android.content.Context;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
/**
* Created by YanZhenjie on 2017/10/3.
*/
public abstract class BaseAdapter<VH extends RecyclerView.ViewHolder, T> extends RecyclerView.Adapter<VH> {
private IItemClickListener<T> mItemClickListener;
protected IAdapterContextMenu<T> adapterContextMenu;
protected List<T> mDataList;
protected Context mContext;
private final LayoutInflater mInflater;
public BaseAdapter(Context context) {
this.mContext = context;
this.mInflater = LayoutInflater.from(context);
}
public LayoutInflater getInflater() {
return mInflater;
}
@Override
public int getItemCount() {
return mDataList == null ? 0 : mDataList.size();
}
public T getBean(int position) {
return mDataList.get(position);
}
@Override
public void onBindViewHolder(@NonNull VH holder, int position) {
holder.itemView.setOnClickListener(v -> {
if (mItemClickListener != null) {
mItemClickListener.clickItem(mDataList.get(holder.getBindingAdapterPosition()));
}
});
holder.itemView.setOnCreateContextMenuListener((menu, v, menuInfo) ->
createMenu(menu, mDataList.get(holder.getBindingAdapterPosition())));
onBindViewHolderDo(holder, position);
}
protected abstract void onBindViewHolderDo(VH holder, int position);
protected void createMenu(ContextMenu menu, T bean) {
if (adapterContextMenu == null) return;
menu.clear();
for (int index = 0; index < adapterContextMenu.menuItemSize(); index++) {
menu.add(0, index, 0, adapterContextMenu.menuItemName(index));
MenuItem menuItem = menu.getItem(index);
if (menuItem != null) {
menuItem.setOnMenuItemClickListener(item ->
adapterContextMenu.onMenuItemClick(bean, item));
}
}
}
public void setAdapterContextMenu(IAdapterContextMenu<T> adapterContextMenu) {
this.adapterContextMenu = adapterContextMenu;
}
public void setOnItemClickListener(IItemClickListener<T> mItemClickListener) {
this.mItemClickListener = mItemClickListener;
}
public void refreshData(List<T> dataList) {
this.mDataList = dataList;
notifyDataSetChanged();
}
}

@ -0,0 +1,14 @@
package com.project.survey.adapter.base;
import android.view.MenuItem;
import androidx.annotation.StringRes;
public interface IAdapterContextMenu<T> {
int menuItemSize();
@StringRes
int menuItemName(int index);
boolean onMenuItemClick(T item, MenuItem menuItem);
}

@ -0,0 +1,5 @@
package com.project.survey.adapter.base;
public interface IItemClickListener<T> {
void clickItem(T t);
}

@ -0,0 +1,15 @@
package com.project.survey.adapter.viewholder;
import androidx.recyclerview.widget.RecyclerView;
import com.project.survey.databinding.ItemProjectListBinding;
public class ProjectListViewHolder extends RecyclerView.ViewHolder {
public final ItemProjectListBinding mBinding;
public ProjectListViewHolder(ItemProjectListBinding binding) {
super(binding.getRoot());
mBinding = binding;
}
}

@ -4,6 +4,7 @@ import android.content.Intent
import android.view.LayoutInflater
import android.view.ViewGroup
import com.project.survey.activity.login.LoginActivity
import com.project.survey.activity.project.ProjectListActivity
import com.project.survey.databinding.FragmentMeBinding
import com.project.survey.fragment.base.BaseFragmentBinding
@ -21,6 +22,10 @@ class MeFragment : BaseFragmentBinding<FragmentMeBinding>() {
startActivity(Intent(context, LoginActivity::class.java))
}
mBinding.tvCompany.setOnClickListener {
startActivity(Intent(requireContext(), ProjectListActivity::class.java))
}
}
override fun initData() {

@ -0,0 +1,4 @@
package com.project.survey.model
class ProjectBean {
}

@ -0,0 +1,383 @@
/*
* Copyright 2017 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.decoration;
import android.graphics.Canvas;
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.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import com.project.survey.widget.swiperecyclerview.widget.ColorDrawer;
import com.project.survey.widget.swiperecyclerview.widget.Drawer;
/**
* Created by YanZhenjie on 2017/8/14.
*/
public class DefaultItemDecoration extends RecyclerView.ItemDecoration {
private final int mWidth;
private final int mHeight;
private final Drawer mDrawer;
/**
* @param color divider line color.
*/
public DefaultItemDecoration(@ColorInt int color) {
this(color, 4, 4);
}
/**
* @param color line color.
* @param width line width.
* @param height line height.
*/
public DefaultItemDecoration(@ColorInt int color, int width, int height) {
this.mWidth = Math.round(width / 2F);
this.mHeight = Math.round(height / 2F);
this.mDrawer = new ColorDrawer(color, mWidth, mHeight);
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent,
@NonNull RecyclerView.State state) {
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
int orientation = getOrientation(layoutManager);
int position = parent.getChildLayoutPosition(view);
int spanCount = getSpanCount(layoutManager);
int childCount = layoutManager.getItemCount();
if (orientation == RecyclerView.VERTICAL) {
offsetVertical(outRect, position, spanCount, childCount);
} else {
offsetHorizontal(outRect, position, spanCount, childCount);
}
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
outRect.set(mWidth, mHeight, mWidth, mHeight); // |-|-
}
}
private void offsetHorizontal(Rect outRect, int position, int spanCount, int childCount) {
boolean firstRaw = isFirstRaw(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean lastRaw = isLastRaw(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean firstColumn = isFirstColumn(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean lastColumn = isLastColumn(RecyclerView.HORIZONTAL, position, spanCount, childCount);
if (spanCount == 1) {
if (firstColumn && lastColumn) { // xxxx
outRect.set(0, 0, 0, 0);
} else if (firstColumn) { // xx|x
outRect.set(0, 0, mWidth, 0);
} else if (lastColumn) { // |xxx
outRect.set(mWidth, 0, 0, 0);
} else { // |x|x
outRect.set(mWidth, 0, mWidth, 0);
}
} else {
if (firstColumn && firstRaw) { // xx|-
outRect.set(0, 0, mWidth, mHeight);
} else if (firstColumn && lastRaw) { // x-|x
outRect.set(0, mHeight, mWidth, 0);
} else if (lastColumn && firstRaw) { // |xx-
outRect.set(mWidth, 0, 0, mHeight);
} else if (lastColumn && lastRaw) { // |-xx
outRect.set(mWidth, mHeight, 0, 0);
} else if (firstColumn) { // x-|-
outRect.set(0, mHeight, mWidth, mHeight);
} else if (lastColumn) { // |-x-
outRect.set(mWidth, mHeight, 0, mHeight);
} else if (firstRaw) { // |x|-
outRect.set(mWidth, 0, mWidth, mHeight);
} else if (lastRaw) { // |-|x
outRect.set(mWidth, mHeight, mWidth, 0);
} else { // |-|-
outRect.set(mWidth, mHeight, mWidth, mHeight);
}
}
}
private void offsetVertical(Rect outRect, int position, int spanCount, int childCount) {
boolean firstRaw = isFirstRaw(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean lastRaw = isLastRaw(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean firstColumn = isFirstColumn(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean lastColumn = isLastColumn(RecyclerView.VERTICAL, position, spanCount, childCount);
if (spanCount == 1) {
if (firstRaw && lastRaw) { // xxxx
outRect.set(0, 0, 0, 0);
} else if (firstRaw) { // xxx-
outRect.set(0, 0, 0, mHeight);
} else if (lastRaw) { // x-xx
outRect.set(0, mHeight, 0, 0);
} else { // x-x-
outRect.set(0, mHeight, 0, mHeight);
}
} else {
if (firstRaw && firstColumn) { // xx|-
outRect.set(0, 0, mWidth, mHeight);
} else if (firstRaw && lastColumn) { // |xx-
outRect.set(mWidth, 0, 0, mHeight);
} else if (lastRaw && firstColumn) { // x-|x
outRect.set(0, mHeight, mWidth, 0);
} else if (lastRaw && lastColumn) { // |-xx
outRect.set(mWidth, mHeight, 0, 0);
} else if (firstRaw) { // |x|-
outRect.set(mWidth, 0, mWidth, mHeight);
} else if (lastRaw) { // |-|x
outRect.set(mWidth, mHeight, mWidth, 0);
} else if (firstColumn) { // x-|-
outRect.set(0, mHeight, mWidth, mHeight);
} else if (lastColumn) { // |-x-
outRect.set(mWidth, mHeight, 0, mHeight);
} else { // |-|-
outRect.set(mWidth, mHeight, mWidth, mHeight);
}
}
}
private int getOrientation(RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof LinearLayoutManager) {
return ((LinearLayoutManager)layoutManager).getOrientation();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
return ((StaggeredGridLayoutManager)layoutManager).getOrientation();
}
return RecyclerView.VERTICAL;
}
private int getSpanCount(RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof GridLayoutManager) {
return ((GridLayoutManager)layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
return ((StaggeredGridLayoutManager)layoutManager).getSpanCount();
}
return 1;
}
private boolean isFirstRaw(int orientation, int position, int columnCount, int childCount) {
if (orientation == RecyclerView.VERTICAL) {
return position < columnCount;
} else {
if (columnCount == 1) return true;
return position % columnCount == 0;
}
}
private boolean isLastRaw(int orientation, int position, int columnCount, int childCount) {
if (orientation == RecyclerView.VERTICAL) {
if (columnCount == 1) {
return position + 1 == childCount;
} else {
int lastRawItemCount = childCount % columnCount;
int rawCount = (childCount - lastRawItemCount) / columnCount + (lastRawItemCount > 0 ? 1 : 0);
int rawPositionJudge = (position + 1) % columnCount;
if (rawPositionJudge == 0) {
int positionRaw = (position + 1) / columnCount;
return rawCount == positionRaw;
} else {
int rawPosition = (position + 1 - rawPositionJudge) / columnCount + 1;
return rawCount == rawPosition;
}
}
} else {
if (columnCount == 1) return true;
return (position + 1) % columnCount == 0;
}
}
private boolean isFirstColumn(int orientation, int position, int columnCount, int childCount) {
if (orientation == RecyclerView.VERTICAL) {
if (columnCount == 1) return true;
return position % columnCount == 0;
} else {
return position < columnCount;
}
}
private boolean isLastColumn(int orientation, int position, int columnCount, int childCount) {
if (orientation == RecyclerView.VERTICAL) {
if (columnCount == 1) return true;
return (position + 1) % columnCount == 0;
} else {
if (columnCount == 1) {
return position + 1 == childCount;
} else {
int lastRawItemCount = childCount % columnCount;
int rawCount = (childCount - lastRawItemCount) / columnCount + (lastRawItemCount > 0 ? 1 : 0);
int rawPositionJudge = (position + 1) % columnCount;
if (rawPositionJudge == 0) {
int positionRaw = (position + 1) / columnCount;
return rawCount == positionRaw;
} else {
int rawPosition = (position + 1 - rawPositionJudge) / columnCount + 1;
return rawCount == rawPosition;
}
}
}
}
@Override
public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
assert layoutManager != null;
int orientation = getOrientation(layoutManager);
int spanCount = getSpanCount(layoutManager);
int childCount = layoutManager.getChildCount();
if (layoutManager instanceof LinearLayoutManager) {
canvas.save();
for (int i = 0; i < childCount; i++) {
View view = layoutManager.getChildAt(i);
assert view != null;
int position = parent.getChildLayoutPosition(view);
if (orientation == RecyclerView.VERTICAL) {
drawVertical(canvas, view, position, spanCount, childCount);
} else {
drawHorizontal(canvas, view, position, spanCount, childCount);
}
}
canvas.restore();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
canvas.save();
for (int i = 0; i < childCount; i++) {
View view = layoutManager.getChildAt(i);
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
}
canvas.restore();
}
}
private void drawHorizontal(Canvas canvas, View view, int position, int spanCount, int childCount) {
boolean firstRaw = isFirstRaw(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean lastRaw = isLastRaw(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean firstColumn = isFirstColumn(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean lastColumn = isLastColumn(RecyclerView.HORIZONTAL, position, spanCount, childCount);
if (spanCount == 1) {
if (firstRaw && lastColumn) { // xxxx
// Nothing.
} else if (firstColumn) { // xx|x
mDrawer.drawRight(view, canvas);
} else if (lastColumn) { // |xxx
mDrawer.drawLeft(view, canvas);
} else { // |x|x
mDrawer.drawLeft(view, canvas);
mDrawer.drawRight(view, canvas);
}
} else {
if (firstColumn && firstRaw) { // xx|-
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (firstColumn && lastRaw) { // x-|x
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
} else if (lastColumn && firstRaw) { // |xx-
mDrawer.drawLeft(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastColumn && lastRaw) { // |-xx
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
} else if (firstColumn) { // x-|-
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastColumn) { // |-x-
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (firstRaw) { // |x|-
mDrawer.drawLeft(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastRaw) { // |-|x
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
} else { // |-|-
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
}
}
}
private void drawVertical(Canvas canvas, View view, int position, int spanCount, int childCount) {
boolean firstRaw = isFirstRaw(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean lastRaw = isLastRaw(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean firstColumn = isFirstColumn(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean lastColumn = isLastColumn(RecyclerView.VERTICAL, position, spanCount, childCount);
if (spanCount == 1) {
if (firstRaw && lastRaw) { // xxxx
// Nothing.
} else if (firstRaw) { // xxx-
mDrawer.drawBottom(view, canvas);
} else if (lastRaw) { // x-xx
mDrawer.drawTop(view, canvas);
} else { // x-x-
mDrawer.drawTop(view, canvas);
mDrawer.drawBottom(view, canvas);
}
} else {
if (firstRaw && firstColumn) { // xx|-
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (firstRaw && lastColumn) { // |xx-
mDrawer.drawLeft(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastRaw && firstColumn) { // x-|x
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
} else if (lastRaw && lastColumn) { // |-xx
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
} else if (firstRaw) { // |x|-
mDrawer.drawLeft(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastRaw) { // |-|x
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
} else if (firstColumn) { // x-|-
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastColumn) { // |-x-
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawBottom(view, canvas);
} else { // |-|-
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
}
}
}
}

@ -0,0 +1,10 @@
package com.project.survey.widget.decoration
import android.content.Context
import androidx.core.content.ContextCompat
import com.project.survey.R
class HorDividerDecoration(context: Context) : DefaultItemDecoration(
ContextCompat.getColor(context, R.color.hor_line),
1, 1
)

@ -1,323 +0,0 @@
package com.project.survey.widget.edittext;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.PasswordTransformationMethod;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.appcompat.widget.AppCompatEditText;
import com.project.survey.R;
import com.project.survey.util.DensityUtils;
import com.project.survey.util.ResUtils;
/**
* 支持显示密码的输入框
*
* @author xuexiang
* @since 2019/1/14 下午10:08
*/
public class PasswordEditText extends AppCompatEditText {
/**
* 增大点击区域
*/
private int mExtraClickArea;
private final static int ALPHA_ICON_ENABLED = (int) (255 * 0.54f);
private final static int ALPHA_ICON_DISABLED = (int) (255 * 0.38f);
private Drawable mShowPwDrawable;
private Drawable mHidePwDrawable;
private boolean mPasswordVisible;
private boolean mShowingIcon;
private boolean mSetErrorCalled;
private boolean mHoverShowsPw;
private boolean mHandlingHoverEvent;
private PasswordTransformationMethod mTransformationMethod;
public PasswordEditText(Context context) {
this(context, null);
}
public PasswordEditText(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.PasswordEditTextStyle);
}
public PasswordEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context, attrs, defStyleAttr);
}
public void initAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
mExtraClickArea = DensityUtils.dp2px(context, 20);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PasswordEditText, defStyleAttr, 0);
boolean useNonMonospaceFont;
boolean enableIconAlpha;
try {
mShowPwDrawable = ResUtils.getDrawableAttrRes(getContext(), typedArray, R.styleable.PasswordEditText_pet_iconShow);
if (mShowPwDrawable == null) {
mShowPwDrawable = ResUtils.getVectorDrawable(getContext(), R.drawable.pet_icon_visibility_24dp);
}
mHidePwDrawable = ResUtils.getDrawableAttrRes(getContext(), typedArray, R.styleable.PasswordEditText_pet_iconHide);
if (mHidePwDrawable == null) {
mHidePwDrawable = ResUtils.getVectorDrawable(getContext(), R.drawable.pet_icon_visibility_off_24dp);
}
mHoverShowsPw = typedArray.getBoolean(R.styleable.PasswordEditText_pet_hoverShowsPw, false);
useNonMonospaceFont = typedArray.getBoolean(R.styleable.PasswordEditText_pet_nonMonospaceFont, false);
enableIconAlpha = typedArray.getBoolean(R.styleable.PasswordEditText_pet_enableIconAlpha, true);
boolean isAsteriskStyle = typedArray.getBoolean(R.styleable.PasswordEditText_pet_isAsteriskStyle, false);
if (isAsteriskStyle) {
mTransformationMethod = AsteriskPasswordTransformationMethod.getInstance();
} else {
mTransformationMethod = PasswordTransformationMethod.getInstance();
}
} finally {
typedArray.recycle();
}
if (enableIconAlpha) {
mHidePwDrawable.setAlpha(ALPHA_ICON_ENABLED);
mShowPwDrawable.setAlpha(ALPHA_ICON_DISABLED);
}
if (useNonMonospaceFont) {
setTypeface(Typeface.DEFAULT);
}
addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence seq, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (s.length() > 0) {
if (mSetErrorCalled) {
setCompoundDrawablesRelative(null, null, null, null);
mSetErrorCalled = false;
showPasswordVisibilityIndicator(true);
}
if (!mShowingIcon) {
showPasswordVisibilityIndicator(true);
}
} else {
// hides the indicator if no text inside text field
mPasswordVisible = false;
handlePasswordInputVisibility();
showPasswordVisibilityIndicator(false);
}
}
});
handlePasswordInputVisibility();
}
public PasswordEditText setExtraClickAreaSize(int extraClickArea) {
mExtraClickArea = extraClickArea;
return this;
}
/**
* 设置密码输入框的样式
*
* @param transformationMethod
* @return
*/
public PasswordEditText setPasswordTransformationMethod(PasswordTransformationMethod transformationMethod) {
mTransformationMethod = transformationMethod;
return this;
}
/**
* 设置密码输入框的样式
*
* @param isAsteriskStyle
* @return
*/
public PasswordEditText setIsAsteriskStyle(boolean isAsteriskStyle) {
if (isAsteriskStyle) {
mTransformationMethod = AsteriskPasswordTransformationMethod.getInstance();
} else {
mTransformationMethod = PasswordTransformationMethod.getInstance();
}
return this;
}
private boolean isRtl() {
return getLayoutDirection() == LAYOUT_DIRECTION_RTL;
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
return new SavedState(superState, mShowingIcon, mPasswordVisible);
}
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
mShowingIcon = savedState.isShowingIcon();
mPasswordVisible = savedState.isPasswordVisible();
handlePasswordInputVisibility();
showPasswordVisibilityIndicator(mShowingIcon);
}
@Override
public void setError(CharSequence error) {
super.setError(error);
mSetErrorCalled = true;
}
@Override
public void setError(CharSequence error, Drawable icon) {
super.setError(error, icon);
mSetErrorCalled = true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mShowingIcon) {
return super.onTouchEvent(event);
} else {
boolean touchable = isTouchable(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mHoverShowsPw) {
if (touchable) {
togglePasswordIconVisibility();
// prevent keyboard from coming up
event.setAction(MotionEvent.ACTION_CANCEL);
mHandlingHoverEvent = true;
}
}
break;
case MotionEvent.ACTION_UP:
if (mHandlingHoverEvent || touchable) {
togglePasswordIconVisibility();
// prevent keyboard from coming up
event.setAction(MotionEvent.ACTION_CANCEL);
mHandlingHoverEvent = false;
}
break;
default:
break;
}
return super.onTouchEvent(event);
}
}
private boolean isTouchable(MotionEvent event) {
boolean touchable;
if (isRtl()) {
touchable = event.getX() > getPaddingLeft() - mExtraClickArea && event.getX() < getPaddingLeft() + mShowPwDrawable.getIntrinsicWidth() + mExtraClickArea;
} else {
touchable = event.getX() > getWidth() - getPaddingRight() - mShowPwDrawable.getIntrinsicWidth() - mExtraClickArea && event.getX() < getWidth() - getPaddingRight() + mExtraClickArea;
}
return touchable;
}
private void showPasswordVisibilityIndicator(boolean shouldShowIcon) {
if (shouldShowIcon) {
Drawable drawable = mPasswordVisible ? mShowPwDrawable : mHidePwDrawable;
mShowingIcon = true;
setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, drawable, null);
} else {
// reset drawable
setCompoundDrawablesRelative(null, null, null, null);
mShowingIcon = false;
}
}
/**
* This method toggles the visibility of the icon and takes care of switching the input type
* of the view to be able to see the password afterwards.
* <p>
* This method may only be called if there is an icon visible
*/
private void togglePasswordIconVisibility() {
mPasswordVisible = !mPasswordVisible;
handlePasswordInputVisibility();
showPasswordVisibilityIndicator(true);
}
/**
* This method is called when restoring the state (e.g. on orientation change)
*/
private void handlePasswordInputVisibility() {
int selectionStart = getSelectionStart();
int selectionEnd = getSelectionEnd();
if (mPasswordVisible) {
setTransformationMethod(null);
} else {
setTransformationMethod(mTransformationMethod);
}
setSelection(selectionStart, selectionEnd);
}
/**
* Convenience class to save / restore the state of icon.
*/
protected static class SavedState extends BaseSavedState {
private final boolean mShowingIcon;
private final boolean mPasswordVisible;
private SavedState(Parcelable superState, boolean sI, boolean pV) {
super(superState);
mShowingIcon = sI;
mPasswordVisible = pV;
}
private SavedState(Parcel in) {
super(in);
mShowingIcon = in.readByte() != 0;
mPasswordVisible = in.readByte() != 0;
}
public boolean isShowingIcon() {
return mShowingIcon;
}
public boolean isPasswordVisible() {
return mPasswordVisible;
}
@Override
public void writeToParcel(Parcel destination, int flags) {
super.writeToParcel(destination, flags);
destination.writeByte((byte) (mShowingIcon ? 1 : 0));
destination.writeByte((byte) (mPasswordVisible ? 1 : 0));
}
public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}

@ -0,0 +1,57 @@
package com.project.survey.widget.edittext;
import android.content.Context;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.project.survey.R;
public class PasswordView extends RelativeLayout {
private EditText etPwd;
private ImageView ivPwdShowHide;
public PasswordView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public PasswordView(Context context) {
super(context);
init(context);
}
private void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.view_password, this, true);
etPwd = findViewById(R.id.etPwd);
View pwdShowHide = findViewById(R.id.pwdShowHide);
ivPwdShowHide = findViewById(R.id.ivPwdShowHide);
pwdShowHide.setOnClickListener(v -> {
ivPwdShowHide.setSelected(!ivPwdShowHide.isSelected());
if (ivPwdShowHide.isSelected()) {
etPwd.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
} else {
etPwd.setTransformationMethod(PasswordTransformationMethod.getInstance());
}
etPwd.setSelection(etPwd.getText().length());
});
}
public String getText() {
return etPwd.getText().toString().trim();
}
public void clearEtPwdText() {
etPwd.setText("");
}
}

@ -0,0 +1,351 @@
/*
* Copyright 2017 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
import static com.project.survey.widget.swiperecyclerview.SwipeRecyclerView.LEFT_DIRECTION;
import static com.project.survey.widget.swiperecyclerview.SwipeRecyclerView.RIGHT_DIRECTION;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.collection.SparseArrayCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import com.project.survey.R;
import java.lang.reflect.Field;
import java.util.List;
/**
* Created by YanZhenjie on 2017/7/20.
*/
class AdapterWrapper extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int BASE_ITEM_TYPE_HEADER = 100000;
private static final int BASE_ITEM_TYPE_FOOTER = 200000;
private SparseArrayCompat<View> mHeaderViews = new SparseArrayCompat<>();
private SparseArrayCompat<View> mFootViews = new SparseArrayCompat<>();
private RecyclerView.Adapter mAdapter;
private LayoutInflater mInflater;
private SwipeMenuCreator mSwipeMenuCreator;
private OnItemMenuClickListener mOnItemMenuClickListener;
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
AdapterWrapper(Context context, RecyclerView.Adapter adapter) {
this.mInflater = LayoutInflater.from(context);
this.mAdapter = adapter;
}
public RecyclerView.Adapter getOriginAdapter() {
return mAdapter;
}
/**
* Set to create menu listener.
*
* @param swipeMenuCreator listener.
*/
void setSwipeMenuCreator(SwipeMenuCreator swipeMenuCreator) {
this.mSwipeMenuCreator = swipeMenuCreator;
}
/**
* Set to click menu listener.
*
* @param onItemMenuClickListener listener.
*/
void setOnItemMenuClickListener(OnItemMenuClickListener onItemMenuClickListener) {
this.mOnItemMenuClickListener = onItemMenuClickListener;
}
void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.mOnItemClickListener = onItemClickListener;
}
void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) {
this.mOnItemLongClickListener = onItemLongClickListener;
}
@Override
public int getItemCount() {
return getHeaderCount() + getContentItemCount() + getFooterCount();
}
private int getContentItemCount() {
return mAdapter.getItemCount();
}
@Override
public int getItemViewType(int position) {
if (isHeader(position)) {
return mHeaderViews.keyAt(position);
} else if (isFooter(position)) {
return mFootViews.keyAt(position - getHeaderCount() - getContentItemCount());
}
return mAdapter.getItemViewType(position - getHeaderCount());
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View contentView = mHeaderViews.get(viewType);
if (contentView != null) {
return new ViewHolder(contentView);
}
contentView = mFootViews.get(viewType);
if (contentView != null) {
return new ViewHolder(contentView);
}
final RecyclerView.ViewHolder viewHolder = mAdapter.onCreateViewHolder(parent, viewType);
if (mOnItemClickListener != null) {
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(v, viewHolder.getAdapterPosition());
}
});
}
if (mOnItemLongClickListener != null) {
viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mOnItemLongClickListener.onItemLongClick(v, viewHolder.getAdapterPosition());
return true;
}
});
}
if (mSwipeMenuCreator == null) return viewHolder;
contentView = mInflater.inflate(R.layout.x_recycler_view_item, parent, false);
ViewGroup viewGroup = contentView.findViewById(R.id.swipe_content);
viewGroup.addView(viewHolder.itemView);
try {
Field itemView = getSupperClass(viewHolder.getClass()).getDeclaredField("itemView");
if (!itemView.isAccessible()) itemView.setAccessible(true);
itemView.set(viewHolder, contentView);
} catch (Exception ignored) {
}
return viewHolder;
}
private Class<?> getSupperClass(Class<?> aClass) {
Class<?> supperClass = aClass.getSuperclass();
if (supperClass != null && !supperClass.equals(Object.class)) {
return getSupperClass(supperClass);
}
return aClass;
}
@Override
public final void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
}
@Override
public final void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position,
@NonNull List<Object> payloads) {
if (isHeaderOrFooter(holder)) return;
View itemView = holder.itemView;
position -= getHeaderCount();
if (itemView instanceof SwipeMenuLayout && mSwipeMenuCreator != null) {
SwipeMenuLayout menuLayout = (SwipeMenuLayout) itemView;
SwipeMenu leftMenu = new SwipeMenu(menuLayout);
SwipeMenu rightMenu = new SwipeMenu(menuLayout);
mSwipeMenuCreator.onCreateMenu(leftMenu, rightMenu, position);
SwipeMenuView leftMenuView = (SwipeMenuView) menuLayout.getChildAt(0);
if (leftMenu.hasMenuItems()) {
leftMenuView.setOrientation(leftMenu.getOrientation());
leftMenuView.createMenu(holder, leftMenu, menuLayout, LEFT_DIRECTION, mOnItemMenuClickListener);
} else if (leftMenuView.getChildCount() > 0) {
leftMenuView.removeAllViews();
}
SwipeMenuView rightMenuView = (SwipeMenuView) menuLayout.getChildAt(2);
if (rightMenu.hasMenuItems()) {
rightMenuView.setOrientation(rightMenu.getOrientation());
rightMenuView.createMenu(holder, rightMenu, menuLayout, RIGHT_DIRECTION, mOnItemMenuClickListener);
} else if (rightMenuView.getChildCount() > 0) {
rightMenuView.removeAllViews();
}
}
mAdapter.onBindViewHolder(holder, position, payloads);
}
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
mAdapter.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
if (lm instanceof GridLayoutManager) {
final GridLayoutManager glm = (GridLayoutManager) lm;
final GridLayoutManager.SpanSizeLookup originLookup = glm.getSpanSizeLookup();
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (isHeaderOrFooter(position)) return glm.getSpanCount();
if (originLookup != null) return originLookup.getSpanSize(position);
return 1;
}
});
}
}
@Override
public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
if (isHeaderOrFooter(holder)) {
ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
if (lp instanceof StaggeredGridLayoutManager.LayoutParams) {
StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
p.setFullSpan(true);
}
} else {
mAdapter.onViewAttachedToWindow(holder);
}
}
public boolean isHeaderOrFooter(RecyclerView.ViewHolder holder) {
if (holder instanceof ViewHolder) return true;
return isHeaderOrFooter(holder.getAdapterPosition());
}
public boolean isHeaderOrFooter(int position) {
return isHeader(position) || isFooter(position);
}
public boolean isHeader(int position) {
return position >= 0 && position < getHeaderCount();
}
public boolean isFooter(int position) {
return position >= getHeaderCount() + getContentItemCount();
}
public void addHeaderView(View view) {
mHeaderViews.put(getHeaderCount() + BASE_ITEM_TYPE_HEADER, view);
}
public void addHeaderViewAndNotify(View view) {
addHeaderView(view);
notifyItemInserted(getHeaderCount() - 1);
}
public void removeHeaderViewAndNotify(View view) {
int headerIndex = mHeaderViews.indexOfValue(view);
if (headerIndex == -1) return;
mHeaderViews.removeAt(headerIndex);
notifyItemRemoved(headerIndex);
}
public void addFooterView(View view) {
mFootViews.put(getFooterCount() + BASE_ITEM_TYPE_FOOTER, view);
}
public void addFooterViewAndNotify(View view) {
addFooterView(view);
notifyItemInserted(getHeaderCount() + getContentItemCount() + getFooterCount() - 1);
}
public void removeFooterViewAndNotify(View view) {
int footerIndex = mFootViews.indexOfValue(view);
if (footerIndex == -1) return;
mFootViews.removeAt(footerIndex);
notifyItemRemoved(getHeaderCount() + getContentItemCount() + footerIndex);
}
public int getHeaderCount() {
return mHeaderViews.size();
}
public int getFooterCount() {
return mFootViews.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
}
@Override
public final void setHasStableIds(boolean hasStableIds) {
super.setHasStableIds(hasStableIds);
}
@Override
public long getItemId(int position) {
if (isHeaderOrFooter(position)) {
return -position - 1;
}
position -= getHeaderCount();
return mAdapter.getItemId(position);
}
@Override
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
if (!isHeaderOrFooter(holder)) mAdapter.onViewRecycled(holder);
}
@Override
public boolean onFailedToRecycleView(@NonNull RecyclerView.ViewHolder holder) {
if (!isHeaderOrFooter(holder)) return mAdapter.onFailedToRecycleView(holder);
return false;
}
@Override
public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) {
if (!isHeaderOrFooter(holder)) mAdapter.onViewDetachedFromWindow(holder);
}
@Override
public void registerAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) {
super.registerAdapterDataObserver(observer);
}
@Override
public void unregisterAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) {
super.unregisterAdapterDataObserver(observer);
}
@Override
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
mAdapter.onDetachedFromRecyclerView(recyclerView);
}
}

@ -0,0 +1,139 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
/**
* Created by Yan Zhenjie on 2016/7/27.
*/
interface Controller {
/**
* The menu is open?
*
* @return true, otherwise false.
*/
boolean isMenuOpen();
/**
* The menu is open on the left?
*
* @return true, otherwise false.
*/
boolean isLeftMenuOpen();
/**
* The menu is open on the right?
*
* @return true, otherwise false.
*/
boolean isRightMenuOpen();
/**
* The menu is completely open?
*
* @return true, otherwise false.
*/
boolean isCompleteOpen();
/**
* The menu is completely open on the left?
*
* @return true, otherwise false.
*/
boolean isLeftCompleteOpen();
/**
* The menu is completely open on the right?
*
* @return true, otherwise false.
*/
boolean isRightCompleteOpen();
/**
* The menu is open?
*
* @return true, otherwise false.
*/
boolean isMenuOpenNotEqual();
/**
* The menu is open on the left?
*
* @return true, otherwise false.
*/
boolean isLeftMenuOpenNotEqual();
/**
* The menu is open on the right?
*
* @return true, otherwise false.
*/
boolean isRightMenuOpenNotEqual();
/**
* Open the current menu.
*/
void smoothOpenMenu();
/**
* Open the menu on left.
*/
void smoothOpenLeftMenu();
/**
* Open the menu on right.
*/
void smoothOpenRightMenu();
/**
* Open the menu on left for the duration.
*
* @param duration duration time.
*/
void smoothOpenLeftMenu(int duration);
/**
* Open the menu on right for the duration.
*
* @param duration duration time.
*/
void smoothOpenRightMenu(int duration);
// ---------- closeMenu. ---------- //
/**
* Smooth closed the menu.
*/
void smoothCloseMenu();
/**
* Smooth closed the menu on the left.
*/
void smoothCloseLeftMenu();
/**
* Smooth closed the menu on the right.
*/
void smoothCloseRightMenu();
/**
* Smooth closed the menu for the duration.
*
* @param duration duration time.
*/
void smoothCloseMenu(int duration);
}

@ -0,0 +1,472 @@
/*
* Copyright 2019 Zhenjie Yan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Zhenjie Yan on 1/28/19.
*/
public abstract class ExpandableAdapter<VH extends ExpandableAdapter.ViewHolder> extends RecyclerView.Adapter<VH> {
private static final int TYPE_PARENT = 10000000;
private static final int TYPE_CHILD = 20000000;
private final SparseBooleanArray mExpandItemArray = new SparseBooleanArray();
private final List<Integer> mParentViewType = new ArrayList<>();
/**
* Parent item is expanded.
*
* @param parentPosition position of parent item.
*
* @return true, otherwise is false.
*/
public final boolean isExpanded(int parentPosition) {
return mExpandItemArray.get(parentPosition, false);
}
/**
* Expand parent.
*
* @param parentPosition position of parent item.
*/
public final void expandParent(int parentPosition) {
if (!isExpanded(parentPosition)) {
mExpandItemArray.append(parentPosition, true);
int position = positionFromParentPosition(parentPosition);
int childCount = childItemCount(parentPosition);
notifyItemRangeInserted(position + 1, childCount);
}
}
/**
* Collapse parent.
*
* @param parentPosition position of parent item.
*/
public final void collapseParent(int parentPosition) {
if (isExpanded(parentPosition)) {
mExpandItemArray.append(parentPosition, false);
int position = positionFromParentPosition(parentPosition);
int childCount = childItemCount(parentPosition);
notifyItemRangeRemoved(position + 1, childCount);
}
}
/**
* Notify any registered observers that the item at <code>parentPosition</code> has changed.
*
* @param parentPosition position of parent item.
*/
public final void notifyParentChanged(int parentPosition) {
int position = positionFromParentPosition(parentPosition);
notifyItemChanged(position);
}
/**
* Notify any registered observers that the item reflected at <code>parentPosition</code> has been newly inserted.
*
* @param parentPosition position of parent item.
*/
public final void notifyParentInserted(int parentPosition) {
int position = positionFromParentPosition(parentPosition);
notifyItemInserted(position);
}
/**
* Notify any registered observers that the item previously located at <code>parentPosition</code> has been removed
* from the data set.
*
* @param parentPosition position of parent item.
*/
public final void notifyParentRemoved(int parentPosition) {
int position = positionFromParentPosition(parentPosition);
notifyItemRemoved(position);
}
/**
* Notify any registered observers that the item at <code>parentPosition, childPosition</code> has changed.
*
* @param parentPosition position of parent item.
* @param childPosition positoin of child item.
*/
public final void notifyChildChanged(int parentPosition, int childPosition) {
int position = positionFromChildPosition(parentPosition, childPosition);
notifyItemChanged(position);
}
/**
* Notify any registered observers that the item reflected at <code>parentPosition, childPosition</code> has been
* newly inserted.
*
* @param parentPosition position of parent item.
* @param childPosition positoin of child item.
*/
public final void notifyChildInserted(int parentPosition, int childPosition) {
int position = positionFromChildPosition(parentPosition, childPosition);
notifyItemInserted(position);
}
/**
* Notify any registered observers that the item previously located at <code>parentPosition, childPosition</code>
* has been removed from the data set.
*
* @param parentPosition position of parent item.
* @param childPosition positoin of child item.
*/
public final void notifyChildRemoved(int parentPosition, int childPosition) {
int position = positionFromChildPosition(parentPosition, childPosition);
notifyItemRemoved(position);
}
private int positionFromParentPosition(int parentPosition) {
int itemCount = 0;
int parentCount = parentItemCount();
for (int i = 0; i < parentCount; i++) {
itemCount += 1;
if (parentPosition == i) {
return itemCount - 1;
} else {
if (isExpanded(i)) {
itemCount += childItemCount(i);
} else {
// itemCount += 1;
}
}
}
throw new IllegalStateException("The parent position is invalid: " + parentPosition);
}
private int positionFromChildPosition(int parentPosition, int childPosition) {
int itemCount = 0;
int parentCount = parentItemCount();
for (int i = 0; i < parentCount; i++) {
itemCount += 1;
if (parentPosition == i) {
int childCount = childItemCount(parentPosition);
if (childPosition < childCount) {
itemCount += (childPosition + 1);
return itemCount - 1;
}
throw new IllegalStateException("The child position is invalid: " + childPosition);
} else {
if (isExpanded(i)) {
itemCount += childItemCount(i);
} else {
// itemCount += 1;
}
}
}
throw new IllegalStateException("The parent position is invalid: " + parentPosition);
}
@Override
public final int getItemCount() {
int parentCount = parentItemCount();
for (int i = 0; i < parentCount; i++) {
if (isExpanded(i)) {
int childCount = childItemCount(i);
parentCount += childCount;
} else {
// parentCount += 0;
}
}
return parentCount;
}
/**
* Get the total number of items in the parent.
*/
public abstract int parentItemCount();
/**
* Get the total number of child items under parent.
*
* @param parentPosition position of parent item.
*/
public abstract int childItemCount(int parentPosition);
@Override
public final int getItemViewType(int position) {
int parentPosition = parentItemPosition(position);
if (isParentItem(position)) {
int viewType = parentItemViewType(parentPosition);
if (!mParentViewType.contains(viewType)) mParentViewType.add(viewType);
return viewType;
} else {
int childPosition = childItemPosition(position);
return childItemViewType(parentPosition, childPosition);
}
}
/**
* Get the view type of the parent item.
*
* @param parentPosition position of parent item.
*/
public int parentItemViewType(int parentPosition) {
return TYPE_PARENT;
}
/**
* Get the view type of the child item.
*
* @param parentPosition position of parent item.
* @param childPosition position of child item.
*/
public int childItemViewType(int parentPosition, int childPosition) {
return TYPE_CHILD;
}
/**
* Item is a parent item.
*
* @param adapterPosition adapter position.
*
* @return true, otherwise is false.
*/
public final boolean isParentItem(int adapterPosition) {
int itemCount = 0;
int parentCount = parentItemCount();
for (int i = 0; i < parentCount; i++) {
if (itemCount == adapterPosition) {
return true;
}
itemCount += 1;
if (isExpanded(i)) {
itemCount += childItemCount(i);
} else {
// itemCount += 1;
}
}
return false;
}
/**
* Get the position of the parent item from the adapter position.
*
* @param adapterPosition adapter position of item.
*/
public final int parentItemPosition(int adapterPosition) {
int itemCount = 0;
for (int i = 0; i < parentItemCount(); i++) {
itemCount += 1;
if (isExpanded(i)) {
int childCount = childItemCount(i);
itemCount += childCount;
}
if (adapterPosition < itemCount) {
return i;
}
}
throw new IllegalStateException("The adapter position is not a parent type: " + adapterPosition);
}
/**
* Get the position of the child item from the adapter position.
*
* @param childAdapterPosition adapter position of child item.
*/
public final int childItemPosition(int childAdapterPosition) {
int itemCount = 0;
int parentCount = parentItemCount();
for (int i = 0; i < parentCount; i++) {
itemCount += 1;
if (isExpanded(i)) {
int childCount = childItemCount(i);
itemCount += childCount;
if (childAdapterPosition < itemCount) {
return childCount - (itemCount - childAdapterPosition);
}
} else {
// itemCount += 1;
}
}
throw new IllegalStateException("The adapter position is invalid: " + childAdapterPosition);
}
@NonNull
@Override
public final VH onCreateViewHolder(@NonNull ViewGroup root, int viewType) {
if (mParentViewType.contains(viewType)) return createParentHolder(root, viewType);
return createChildHolder(root, viewType);
}
/**
* Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent an parent item.
*
* @param root the ViewGroup into which the new View will be added after it is bound to an adapter position.
* @param viewType The view type of the new View.
*
* @return a new {@link ViewHolder} that holds a View of the given view type.
*/
public abstract VH createParentHolder(@NonNull ViewGroup root, int viewType);
/**
* Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent an child item.
*
* @param root the ViewGroup into which the new View will be added after it is bound to an adapter position.
* @param viewType The view type of the new View.
*
* @return a new {@link ViewHolder} that holds a View of the given view type.
*/
public abstract VH createChildHolder(@NonNull ViewGroup root, int viewType);
@Override
public final void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) {
int parentPosition = parentItemPosition(position);
if (isParentItem(position)) {
bindParentHolder(holder, parentPosition, payloads);
} else {
int childPosition = childItemPosition(position);
bindChildHolder(holder, parentPosition, childPosition, payloads);
}
}
public void bindParentHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) {
bindParentHolder(holder, position);
}
public void bindChildHolder(@NonNull VH holder, int parentPosition, int position, @NonNull List<Object> payloads) {
bindChildHolder(holder, parentPosition, position);
}
/**
* Called by {@link RecyclerView} to display the data at the specified position. This method should update the
* contents of the {@link ViewHolder#itemView} to reflect the item at the given position.
*
* @param holder parent holder.
* @param position position of parent item.
*/
public abstract void bindParentHolder(@NonNull VH holder, int position);
/**
* Called by {@link RecyclerView} to display the data at the specified position. This method should update the *
* contents of the {@link ViewHolder#itemView} to reflect the item at the given position.
*
* @param holder child holder.
* @param parentPosition position of parent item.
* @param position position of child position.
*/
public abstract void bindChildHolder(@NonNull VH holder, int parentPosition, int position);
@Deprecated
@Override
public final void onBindViewHolder(@NonNull VH holder, int position) {
}
public static abstract class ViewHolder extends RecyclerView.ViewHolder {
private ExpandableAdapter mAdapter;
public ViewHolder(@NonNull View itemView, ExpandableAdapter adapter) {
super(itemView);
this.mAdapter = adapter;
}
/**
* Determine if the current viewholder is a parent item.
*
* @return true, otherwise is false.
*/
public final boolean isParentItem() {
return mAdapter.isParentItem(getAdapterPosition());
}
/**
* Get the position of parent item.
*/
public final int parentItemPosition() {
return mAdapter.parentItemPosition(getAdapterPosition());
}
/**
* Get the position of child item.
*/
public final int childItemPosition() {
if (isParentItem()) throw new IllegalStateException("This item is not a child item.");
return mAdapter.childItemPosition(getAdapterPosition());
}
/**
* Parent item is expanded.
*
* @return true, otherwise is false.
*/
public final boolean isParentExpanded() {
return mAdapter.isExpanded(parentItemPosition());
}
}
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
if (lm instanceof GridLayoutManager) {
final GridLayoutManager glm = (GridLayoutManager)lm;
final GridLayoutManager.SpanSizeLookup originLookup = glm.getSpanSizeLookup();
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (isParentItem(position)) return glm.getSpanCount();
if (originLookup != null) return originLookup.getSpanSize(position);
return 1;
}
});
}
}
@Override
public void onViewAttachedToWindow(@NonNull VH holder) {
if (isParentItem(holder.getAdapterPosition())) {
ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
if (lp instanceof StaggeredGridLayoutManager.LayoutParams) {
StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams)lp;
p.setFullSpan(true);
}
}
}
}

@ -0,0 +1,80 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
import android.view.View;
import android.view.ViewGroup;
import android.widget.OverScroller;
/**
* Created by Yan Zhenjie on 2016/7/22.
*/
abstract class Horizontal {
private int direction;
private View menuView;
protected Checker mChecker;
public Horizontal(int direction, View menuView) {
this.direction = direction;
this.menuView = menuView;
mChecker = new Checker();
}
public boolean canSwipe() {
if (menuView instanceof ViewGroup) {
return ((ViewGroup)menuView).getChildCount() > 0;
}
return false;
}
public boolean isCompleteClose(int scrollX) {
int i = -getMenuView().getWidth() * getDirection();
return scrollX == 0 && i != 0;
}
public abstract boolean isMenuOpen(int scrollX);
public abstract boolean isMenuOpenNotEqual(int scrollX);
public abstract void autoOpenMenu(OverScroller scroller, int scrollX, int duration);
public abstract void autoCloseMenu(OverScroller scroller, int scrollX, int duration);
public abstract Checker checkXY(int x, int y);
public abstract boolean isClickOnContentView(int contentViewWidth, float x);
public int getDirection() {
return direction;
}
public View getMenuView() {
return menuView;
}
public int getMenuWidth() {
return menuView.getWidth();
}
public static final class Checker {
public int x;
public int y;
public boolean shouldResetSwipe;
}
}

@ -0,0 +1,72 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
import android.view.View;
import android.widget.OverScroller;
/**
* Created by Yan Zhenjie on 2016/7/22.
*/
class LeftHorizontal extends Horizontal {
public LeftHorizontal(View menuView) {
super(SwipeRecyclerView.LEFT_DIRECTION, menuView);
}
@Override
public boolean isMenuOpen(int scrollX) {
int i = -getMenuView().getWidth() * getDirection();
return scrollX <= i && i != 0;
}
@Override
public boolean isMenuOpenNotEqual(int scrollX) {
return scrollX < -getMenuView().getWidth() * getDirection();
}
@Override
public void autoOpenMenu(OverScroller scroller, int scrollX, int duration) {
scroller.startScroll(Math.abs(scrollX), 0, getMenuView().getWidth() - Math.abs(scrollX), 0, duration);
}
@Override
public void autoCloseMenu(OverScroller scroller, int scrollX, int duration) {
scroller.startScroll(-Math.abs(scrollX), 0, Math.abs(scrollX), 0, duration);
}
@Override
public Checker checkXY(int x, int y) {
mChecker.x = x;
mChecker.y = y;
mChecker.shouldResetSwipe = false;
if (mChecker.x == 0) {
mChecker.shouldResetSwipe = true;
}
if (mChecker.x >= 0) {
mChecker.x = 0;
}
if (mChecker.x <= -getMenuView().getWidth()) {
mChecker.x = -getMenuView().getWidth();
}
return mChecker;
}
@Override
public boolean isClickOnContentView(int contentViewWidth, float x) {
return x > getMenuView().getWidth();
}
}

@ -0,0 +1,30 @@
/*
* Copyright 2017 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
import android.view.View;
/**
* Created by YanZhenjie on 2017/7/21.
*/
public interface OnItemClickListener {
/**
* @param view target view.
* @param adapterPosition position of item.
*/
void onItemClick(View view, int adapterPosition);
}

@ -0,0 +1,30 @@
/*
* Copyright 2017 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
import android.view.View;
/**
* Created by YanZhenjie on 2017/7/21.
*/
public interface OnItemLongClickListener {
/**
* @param view target view.
* @param adapterPosition position of item.
*/
void onItemLongClick(View view, int adapterPosition);
}

@ -0,0 +1,28 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
/**
* Created by Yan Zhenjie on 2016/7/26.
*/
public interface OnItemMenuClickListener {
/**
* @param menuBridge menu bridge.
* @param adapterPosition position of item.
*/
void onItemClick(SwipeMenuBridge menuBridge, int adapterPosition);
}

@ -0,0 +1,72 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
import android.view.View;
import android.widget.OverScroller;
/**
* Created by Yan Zhenjie on 2016/7/22.
*/
class RightHorizontal extends Horizontal {
public RightHorizontal(View menuView) {
super(SwipeRecyclerView.RIGHT_DIRECTION, menuView);
}
@Override
public boolean isMenuOpen(int scrollX) {
int i = -getMenuView().getWidth() * getDirection();
return scrollX >= i && i != 0;
}
@Override
public boolean isMenuOpenNotEqual(int scrollX) {
return scrollX > -getMenuView().getWidth() * getDirection();
}
@Override
public void autoOpenMenu(OverScroller scroller, int scrollX, int duration) {
scroller.startScroll(Math.abs(scrollX), 0, getMenuView().getWidth() - Math.abs(scrollX), 0, duration);
}
@Override
public void autoCloseMenu(OverScroller scroller, int scrollX, int duration) {
scroller.startScroll(-Math.abs(scrollX), 0, Math.abs(scrollX), 0, duration);
}
@Override
public Checker checkXY(int x, int y) {
mChecker.x = x;
mChecker.y = y;
mChecker.shouldResetSwipe = false;
if (mChecker.x == 0) {
mChecker.shouldResetSwipe = true;
}
if (mChecker.x < 0) {
mChecker.x = 0;
}
if (mChecker.x > getMenuView().getWidth()) {
mChecker.x = getMenuView().getWidth();
}
return mChecker;
}
@Override
public boolean isClickOnContentView(int contentViewWidth, float x) {
return x < (contentViewWidth - getMenuView().getWidth());
}
}

@ -0,0 +1,106 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
import android.widget.LinearLayout;
import androidx.annotation.FloatRange;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Yan Zhenjie on 2016/7/22.
*/
public class SwipeMenu {
@IntDef({HORIZONTAL, VERTICAL})
@Retention(RetentionPolicy.SOURCE)
public @interface OrientationMode {}
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL;
private SwipeMenuLayout mMenuLayout;
private int mOrientation;
private List<SwipeMenuItem> mSwipeMenuItems;
public SwipeMenu(SwipeMenuLayout menuLayout) {
this.mMenuLayout = menuLayout;
this.mOrientation = SwipeMenu.HORIZONTAL;
this.mSwipeMenuItems = new ArrayList<>(2);
}
/**
* Set a percentage.
*
* @param openPercent such as 0.5F.
*/
public void setOpenPercent(@FloatRange(from = 0.1, to = 1) float openPercent) {
mMenuLayout.setOpenPercent(openPercent);
}
/**
* The duration of the set.
*
* @param scrollerDuration such 500.
*/
public void setScrollerDuration(@IntRange(from = 1) int scrollerDuration) {
mMenuLayout.setScrollerDuration(scrollerDuration);
}
/**
* Set the menu mOrientation.
*
* @param orientation use {@link SwipeMenu#HORIZONTAL} or {@link SwipeMenu#VERTICAL}.
*
* @see SwipeMenu#HORIZONTAL
* @see SwipeMenu#VERTICAL
*/
public void setOrientation(@OrientationMode int orientation) {
this.mOrientation = orientation;
}
/**
* Get the menu mOrientation.
*
* @return {@link SwipeMenu#HORIZONTAL} or {@link SwipeMenu#VERTICAL}.
*/
@OrientationMode
public int getOrientation() {
return mOrientation;
}
public void addMenuItem(SwipeMenuItem item) {
mSwipeMenuItems.add(item);
}
public void removeMenuItem(SwipeMenuItem item) {
mSwipeMenuItems.remove(item);
}
public List<SwipeMenuItem> getMenuItems() {
return mSwipeMenuItems;
}
public boolean hasMenuItems() {
return !mSwipeMenuItems.isEmpty();
}
}

@ -0,0 +1,48 @@
/*
* Copyright 2017 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
/**
* Created by YanZhenjie on 2017/7/20.
*/
public class SwipeMenuBridge {
private final Controller mController;
private final int mDirection;
private final int mPosition;
public SwipeMenuBridge(Controller controller, int direction, int position) {
this.mController = controller;
this.mDirection = direction;
this.mPosition = position;
}
@SwipeRecyclerView.DirectionMode
public int getDirection() {
return mDirection;
}
/**
* Get the position of button in the menu.
*/
public int getPosition() {
return mPosition;
}
public void closeMenu() {
mController.smoothCloseMenu();
}
}

@ -0,0 +1,31 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
/**
* Created by Yan Zhenjie on 2016/7/26.
*/
public interface SwipeMenuCreator {
/**
* Create menu for recyclerVie item.
*
* @param leftMenu the menu on the left.
* @param rightMenu the menu on the right.
* @param position the position of item.
*/
void onCreateMenu(SwipeMenu leftMenu, SwipeMenu rightMenu, int position);
}

@ -0,0 +1,166 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
import androidx.core.content.ContextCompat;
/**
* Created by Yan Zhenjie on 2016/7/26.
*/
public class SwipeMenuItem {
private Context mContext;
private Drawable background;
private Drawable icon;
private String title;
private ColorStateList titleColor;
private int titleSize;
private Typeface textTypeface;
private int textAppearance;
private int width = -2;
private int height = -2;
private int weight = 0;
public SwipeMenuItem(Context context) {
mContext = context;
}
public SwipeMenuItem setBackground(@DrawableRes int resId) {
return setBackground(ContextCompat.getDrawable(mContext, resId));
}
public SwipeMenuItem setBackground(Drawable background) {
this.background = background;
return this;
}
public SwipeMenuItem setBackgroundColorResource(@ColorRes int color) {
return setBackgroundColor(ContextCompat.getColor(mContext, color));
}
public SwipeMenuItem setBackgroundColor(@ColorInt int color) {
this.background = new ColorDrawable(color);
return this;
}
public Drawable getBackground() {
return background;
}
public SwipeMenuItem setImage(@DrawableRes int resId) {
return setImage(ContextCompat.getDrawable(mContext, resId));
}
public SwipeMenuItem setImage(Drawable icon) {
this.icon = icon;
return this;
}
public Drawable getImage() {
return icon;
}
public SwipeMenuItem setText(@StringRes int resId) {
return setText(mContext.getString(resId));
}
public SwipeMenuItem setText(String title) {
this.title = title;
return this;
}
public String getText() {
return title;
}
public SwipeMenuItem setTextColorResource(@ColorRes int titleColor) {
return setTextColor(ContextCompat.getColor(mContext, titleColor));
}
public SwipeMenuItem setTextColor(@ColorInt int titleColor) {
this.titleColor = ColorStateList.valueOf(titleColor);
return this;
}
public ColorStateList getTitleColor() {
return titleColor;
}
public SwipeMenuItem setTextSize(int titleSize) {
this.titleSize = titleSize;
return this;
}
public int getTextSize() {
return titleSize;
}
public SwipeMenuItem setTextAppearance(@StyleRes int textAppearance) {
this.textAppearance = textAppearance;
return this;
}
public int getTextAppearance() {
return textAppearance;
}
public SwipeMenuItem setTextTypeface(Typeface textTypeface) {
this.textTypeface = textTypeface;
return this;
}
public Typeface getTextTypeface() {
return textTypeface;
}
public SwipeMenuItem setWidth(int width) {
this.width = width;
return this;
}
public int getWidth() {
return width;
}
public SwipeMenuItem setHeight(int height) {
this.height = height;
return this;
}
public int getHeight() {
return height;
}
public SwipeMenuItem setWeight(int weight) {
this.weight = weight;
return this;
}
public int getWeight() {
return weight;
}
}

@ -0,0 +1,531 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;
import android.widget.OverScroller;
import android.widget.TextView;
import androidx.core.view.ViewCompat;
import com.project.survey.R;
/**
* Created by Yan Zhenjie on 2016/7/27.
*/
public class SwipeMenuLayout extends FrameLayout implements Controller {
public static final int DEFAULT_SCROLLER_DURATION = 200;
private int mLeftViewId = 0;
private int mContentViewId = 0;
private int mRightViewId = 0;
private float mOpenPercent = 0.5f;
private int mScrollerDuration = DEFAULT_SCROLLER_DURATION;
private int mScaledTouchSlop;
private int mLastX;
private int mLastY;
private int mDownX;
private int mDownY;
private View mContentView;
private LeftHorizontal mSwipeLeftHorizontal;
private RightHorizontal mSwipeRightHorizontal;
private Horizontal mSwipeCurrentHorizontal;
private boolean shouldResetSwipe;
private boolean mDragging;
private boolean swipeEnable = true;
private OverScroller mScroller;
private VelocityTracker mVelocityTracker;
private int mScaledMinimumFlingVelocity;
private int mScaledMaximumFlingVelocity;
public SwipeMenuLayout(Context context) {
this(context, null);
}
public SwipeMenuLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SwipeMenuLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setClickable(true);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SwipeMenuLayout);
mLeftViewId = typedArray.getResourceId(R.styleable.SwipeMenuLayout_leftViewId, mLeftViewId);
mContentViewId = typedArray.getResourceId(R.styleable.SwipeMenuLayout_contentViewId, mContentViewId);
mRightViewId = typedArray.getResourceId(R.styleable.SwipeMenuLayout_rightViewId, mRightViewId);
typedArray.recycle();
ViewConfiguration configuration = ViewConfiguration.get(getContext());
mScaledTouchSlop = configuration.getScaledTouchSlop();
mScaledMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
mScaledMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
mScroller = new OverScroller(getContext());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (mLeftViewId != 0 && mSwipeLeftHorizontal == null) {
View view = findViewById(mLeftViewId);
mSwipeLeftHorizontal = new LeftHorizontal(view);
}
if (mRightViewId != 0 && mSwipeRightHorizontal == null) {
View view = findViewById(mRightViewId);
mSwipeRightHorizontal = new RightHorizontal(view);
}
if (mContentViewId != 0 && mContentView == null) {
mContentView = findViewById(mContentViewId);
} else {
TextView errorView = new TextView(getContext());
errorView.setClickable(true);
errorView.setGravity(Gravity.CENTER);
errorView.setTextSize(16);
errorView.setText("You may not have set the ContentView.");
mContentView = errorView;
addView(mContentView);
}
}
/**
* Set whether open swipe. Default is true.
*
* @param swipeEnable true open, otherwise false.
*/
public void setSwipeEnable(boolean swipeEnable) {
this.swipeEnable = swipeEnable;
}
/**
* Open the swipe function of the Item?
*
* @return open is true, otherwise is false.
*/
public boolean isSwipeEnable() {
return swipeEnable;
}
/**
* Set open percentage.
*
* @param openPercent such as 0.5F.
*/
public void setOpenPercent(float openPercent) {
this.mOpenPercent = openPercent;
}
/**
* Get open percentage.
*
* @return such as 0.5F.
*/
public float getOpenPercent() {
return mOpenPercent;
}
/**
* The duration of the set.
*
* @param scrollerDuration such as 500.
*/
public void setScrollerDuration(int scrollerDuration) {
this.mScrollerDuration = scrollerDuration;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean isIntercepted = super.onInterceptTouchEvent(ev);
if (!isSwipeEnable()) {
return isIntercepted;
}
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
mDownX = mLastX = (int) ev.getX();
mDownY = (int) ev.getY();
return false;
}
case MotionEvent.ACTION_MOVE: {
int disX = (int) (ev.getX() - mDownX);
int disY = (int) (ev.getY() - mDownY);
return Math.abs(disX) > mScaledTouchSlop && Math.abs(disX) > Math.abs(disY);
}
case MotionEvent.ACTION_UP: {
boolean isClick = mSwipeCurrentHorizontal != null &&
mSwipeCurrentHorizontal.isClickOnContentView(getWidth(), ev.getX());
if (isMenuOpen() && isClick) {
smoothCloseMenu();
return true;
}
return false;
}
case MotionEvent.ACTION_CANCEL: {
if (!mScroller.isFinished()) mScroller.abortAnimation();
return false;
}
}
return isIntercepted;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (!isSwipeEnable()) {
return super.onTouchEvent(ev);
}
if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(ev);
int dx;
int dy;
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
mLastX = (int) ev.getX();
mLastY = (int) ev.getY();
break;
}
case MotionEvent.ACTION_MOVE: {
int disX = (int) (mLastX - ev.getX());
int disY = (int) (mLastY - ev.getY());
if (!mDragging && Math.abs(disX) > mScaledTouchSlop && Math.abs(disX) > Math.abs(disY)) {
mDragging = true;
}
if (mDragging) {
if (mSwipeCurrentHorizontal == null || shouldResetSwipe) {
if (disX < 0) {
if (mSwipeLeftHorizontal != null) {
mSwipeCurrentHorizontal = mSwipeLeftHorizontal;
} else {
mSwipeCurrentHorizontal = mSwipeRightHorizontal;
}
} else {
if (mSwipeRightHorizontal != null) {
mSwipeCurrentHorizontal = mSwipeRightHorizontal;
} else {
mSwipeCurrentHorizontal = mSwipeLeftHorizontal;
}
}
}
scrollBy(disX, 0);
mLastX = (int) ev.getX();
mLastY = (int) ev.getY();
shouldResetSwipe = false;
}
break;
}
case MotionEvent.ACTION_UP: {
dx = (int) (mDownX - ev.getX());
dy = (int) (mDownY - ev.getY());
mDragging = false;
mVelocityTracker.computeCurrentVelocity(1000, mScaledMaximumFlingVelocity);
int velocityX = (int) mVelocityTracker.getXVelocity();
int velocity = Math.abs(velocityX);
if (velocity > mScaledMinimumFlingVelocity) {
if (mSwipeCurrentHorizontal != null) {
int duration = getSwipeDuration(ev, velocity);
if (mSwipeCurrentHorizontal instanceof RightHorizontal) {
if (velocityX < 0) {
smoothOpenMenu(duration);
} else {
smoothCloseMenu(duration);
}
} else {
if (velocityX > 0) {
smoothOpenMenu(duration);
} else {
smoothCloseMenu(duration);
}
}
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
judgeOpenClose(dx, dy);
}
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
if (Math.abs(mDownX - ev.getX()) > mScaledTouchSlop ||
Math.abs(mDownY - ev.getY()) > mScaledTouchSlop || isLeftMenuOpen() || isRightMenuOpen()) {
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
return true;
}
break;
}
case MotionEvent.ACTION_CANCEL: {
mDragging = false;
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
} else {
dx = (int) (mDownX - ev.getX());
dy = (int) (mDownY - ev.getY());
judgeOpenClose(dx, dy);
}
break;
}
}
return super.onTouchEvent(ev);
}
/**
* compute finish duration.
*
* @param ev up event.
* @param velocity velocity x.
* @return finish duration.
*/
private int getSwipeDuration(MotionEvent ev, int velocity) {
int sx = getScrollX();
int dx = (int) (ev.getX() - sx);
final int width = mSwipeCurrentHorizontal.getMenuWidth();
final int halfWidth = width / 2;
final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
final float distance = halfWidth + halfWidth * distanceInfluenceForSnapDuration(distanceRatio);
int duration;
if (velocity > 0) {
duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
} else {
final float pageDelta = (float) Math.abs(dx) / width;
duration = (int) ((pageDelta + 1) * 100);
}
duration = Math.min(duration, mScrollerDuration);
return duration;
}
float distanceInfluenceForSnapDuration(float f) {
f -= 0.5f; // center the values about 0.
f *= 0.3f * Math.PI / 2.0f;
return (float) Math.sin(f);
}
private void judgeOpenClose(int dx, int dy) {
if (mSwipeCurrentHorizontal != null) {
if (Math.abs(getScrollX()) >=
(mSwipeCurrentHorizontal.getMenuView().getWidth() * mOpenPercent)) { // auto open
if (Math.abs(dx) > mScaledTouchSlop || Math.abs(dy) > mScaledTouchSlop) { // swipe up
if (isMenuOpenNotEqual()) {
smoothCloseMenu();
} else {
smoothOpenMenu();
}
} else { // normal up
if (isMenuOpen()) {
smoothCloseMenu();
} else {
smoothOpenMenu();
}
}
} else { // auto closeMenu
smoothCloseMenu();
}
}
}
@Override
public void scrollTo(int x, int y) {
if (mSwipeCurrentHorizontal == null) {
super.scrollTo(x, y);
} else {
Horizontal.Checker checker = mSwipeCurrentHorizontal.checkXY(x, y);
shouldResetSwipe = checker.shouldResetSwipe;
if (checker.x != getScrollX()) {
super.scrollTo(checker.x, checker.y);
}
}
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset() && mSwipeCurrentHorizontal != null) {
if (mSwipeCurrentHorizontal instanceof RightHorizontal) {
scrollTo(Math.abs(mScroller.getCurrX()), 0);
invalidate();
} else {
scrollTo(-Math.abs(mScroller.getCurrX()), 0);
invalidate();
}
}
}
public boolean hasLeftMenu() {
return mSwipeLeftHorizontal != null && mSwipeLeftHorizontal.canSwipe();
}
public boolean hasRightMenu() {
return mSwipeRightHorizontal != null && mSwipeRightHorizontal.canSwipe();
}
@Override
public boolean isMenuOpen() {
return isLeftMenuOpen() || isRightMenuOpen();
}
@Override
public boolean isLeftMenuOpen() {
return mSwipeLeftHorizontal != null && mSwipeLeftHorizontal.isMenuOpen(getScrollX());
}
@Override
public boolean isRightMenuOpen() {
return mSwipeRightHorizontal != null && mSwipeRightHorizontal.isMenuOpen(getScrollX());
}
@Override
public boolean isCompleteOpen() {
return isLeftCompleteOpen() || isRightMenuOpen();
}
@Override
public boolean isLeftCompleteOpen() {
return mSwipeLeftHorizontal != null && !mSwipeLeftHorizontal.isCompleteClose(getScrollX());
}
@Override
public boolean isRightCompleteOpen() {
return mSwipeRightHorizontal != null && !mSwipeRightHorizontal.isCompleteClose(getScrollX());
}
@Override
public boolean isMenuOpenNotEqual() {
return isLeftMenuOpenNotEqual() || isRightMenuOpenNotEqual();
}
@Override
public boolean isLeftMenuOpenNotEqual() {
return mSwipeLeftHorizontal != null && mSwipeLeftHorizontal.isMenuOpenNotEqual(getScrollX());
}
@Override
public boolean isRightMenuOpenNotEqual() {
return mSwipeRightHorizontal != null && mSwipeRightHorizontal.isMenuOpenNotEqual(getScrollX());
}
@Override
public void smoothOpenMenu() {
smoothOpenMenu(mScrollerDuration);
}
@Override
public void smoothOpenLeftMenu() {
smoothOpenLeftMenu(mScrollerDuration);
}
@Override
public void smoothOpenRightMenu() {
smoothOpenRightMenu(mScrollerDuration);
}
@Override
public void smoothOpenLeftMenu(int duration) {
if (mSwipeLeftHorizontal != null) {
mSwipeCurrentHorizontal = mSwipeLeftHorizontal;
smoothOpenMenu(duration);
}
}
@Override
public void smoothOpenRightMenu(int duration) {
if (mSwipeRightHorizontal != null) {
mSwipeCurrentHorizontal = mSwipeRightHorizontal;
smoothOpenMenu(duration);
}
}
private void smoothOpenMenu(int duration) {
if (mSwipeCurrentHorizontal != null) {
mSwipeCurrentHorizontal.autoOpenMenu(mScroller, getScrollX(), duration);
invalidate();
}
}
@Override
public void smoothCloseMenu() {
smoothCloseMenu(mScrollerDuration);
}
@Override
public void smoothCloseLeftMenu() {
if (mSwipeLeftHorizontal != null) {
mSwipeCurrentHorizontal = mSwipeLeftHorizontal;
smoothCloseMenu();
}
}
@Override
public void smoothCloseRightMenu() {
if (mSwipeRightHorizontal != null) {
mSwipeCurrentHorizontal = mSwipeRightHorizontal;
smoothCloseMenu();
}
}
@Override
public void smoothCloseMenu(int duration) {
if (mSwipeCurrentHorizontal != null) {
mSwipeCurrentHorizontal.autoCloseMenu(mScroller, getScrollX(), duration);
invalidate();
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int contentViewHeight;
if (mContentView != null) {
int contentViewWidth = mContentView.getMeasuredWidthAndState();
contentViewHeight = mContentView.getMeasuredHeightAndState();
LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
int start = getPaddingLeft();
int top = getPaddingTop() + lp.topMargin;
mContentView.layout(start, top, start + contentViewWidth, top + contentViewHeight);
}
if (mSwipeLeftHorizontal != null) {
View leftMenu = mSwipeLeftHorizontal.getMenuView();
int menuViewWidth = leftMenu.getMeasuredWidthAndState();
int menuViewHeight = leftMenu.getMeasuredHeightAndState();
LayoutParams lp = (LayoutParams) leftMenu.getLayoutParams();
int top = getPaddingTop() + lp.topMargin;
leftMenu.layout(-menuViewWidth, top, 0, top + menuViewHeight);
}
if (mSwipeRightHorizontal != null) {
View rightMenu = mSwipeRightHorizontal.getMenuView();
int menuViewWidth = rightMenu.getMeasuredWidthAndState();
int menuViewHeight = rightMenu.getMeasuredHeightAndState();
LayoutParams lp = (LayoutParams) rightMenu.getLayoutParams();
int top = getPaddingTop() + lp.topMargin;
int parentViewWidth = getMeasuredWidthAndState();
rightMenu.layout(parentViewWidth, top, parentViewWidth + menuViewWidth, top + menuViewHeight);
}
}
}

@ -0,0 +1,121 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Typeface;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.view.ViewCompat;
import androidx.core.widget.TextViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
/**
* Created by Yan Zhenjie on 2016/7/26.
*/
public class SwipeMenuView extends LinearLayout implements View.OnClickListener {
private RecyclerView.ViewHolder mViewHolder;
private OnItemMenuClickListener mItemClickListener;
public SwipeMenuView(Context context) {
this(context, null);
}
public SwipeMenuView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SwipeMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setGravity(Gravity.CENTER_VERTICAL);
}
public void createMenu(RecyclerView.ViewHolder viewHolder, SwipeMenu swipeMenu, Controller controller,
int direction, OnItemMenuClickListener itemClickListener) {
removeAllViews();
this.mViewHolder = viewHolder;
this.mItemClickListener = itemClickListener;
List<SwipeMenuItem> items = swipeMenu.getMenuItems();
for (int i = 0; i < items.size(); i++) {
SwipeMenuItem item = items.get(i);
LayoutParams params = new LayoutParams(item.getWidth(), item.getHeight());
params.weight = item.getWeight();
LinearLayout parent = new LinearLayout(getContext());
parent.setId(i);
parent.setGravity(Gravity.CENTER);
parent.setOrientation(VERTICAL);
parent.setLayoutParams(params);
ViewCompat.setBackground(parent, item.getBackground());
parent.setOnClickListener(this);
addView(parent);
SwipeMenuBridge menuBridge = new SwipeMenuBridge(controller, direction, i);
parent.setTag(menuBridge);
if (item.getImage() != null) {
ImageView iv = createIcon(item);
parent.addView(iv);
}
if (!TextUtils.isEmpty(item.getText())) {
TextView tv = createTitle(item);
parent.addView(tv);
}
}
}
@Override
public void onClick(View v) {
if (mItemClickListener != null) {
mItemClickListener.onItemClick((SwipeMenuBridge)v.getTag(), mViewHolder.getAdapterPosition());
}
}
private ImageView createIcon(SwipeMenuItem item) {
ImageView imageView = new ImageView(getContext());
imageView.setImageDrawable(item.getImage());
return imageView;
}
private TextView createTitle(SwipeMenuItem item) {
TextView textView = new TextView(getContext());
textView.setText(item.getText());
textView.setGravity(Gravity.CENTER);
int textSize = item.getTextSize();
if (textSize > 0) textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize);
ColorStateList textColor = item.getTitleColor();
if (textColor != null) textView.setTextColor(textColor);
int textAppearance = item.getTextAppearance();
if (textAppearance != 0) TextViewCompat.setTextAppearance(textView, textAppearance);
Typeface typeface = item.getTextTypeface();
if (typeface != null) textView.setTypeface(typeface);
return textView;
}
}

@ -0,0 +1,864 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import androidx.annotation.IntDef;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import com.project.survey.widget.swiperecyclerview.touch.DefaultItemTouchHelper;
import com.project.survey.widget.swiperecyclerview.touch.OnItemMoveListener;
import com.project.survey.widget.swiperecyclerview.touch.OnItemMovementListener;
import com.project.survey.widget.swiperecyclerview.touch.OnItemStateChangedListener;
import com.project.survey.widget.swiperecyclerview.widget.DefaultLoadMoreView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Yan Zhenjie on 2016/7/27.
*/
public class SwipeRecyclerView extends RecyclerView {
/**
* Left menu.
*/
public static final int LEFT_DIRECTION = 1;
/**
* Right menu.
*/
public static final int RIGHT_DIRECTION = -1;
@IntDef({LEFT_DIRECTION, RIGHT_DIRECTION})
@Retention(RetentionPolicy.SOURCE)
public @interface DirectionMode {
}
/**
* Invalid position.
*/
private static final int INVALID_POSITION = -1;
protected int mScaleTouchSlop;
protected SwipeMenuLayout mOldSwipedLayout;
protected int mOldTouchedPosition = INVALID_POSITION;
private int mDownX;
private int mDownY;
private boolean allowSwipeDelete;
private DefaultItemTouchHelper mItemTouchHelper;
private SwipeMenuCreator mSwipeMenuCreator;
private OnItemMenuClickListener mOnItemMenuClickListener;
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
private AdapterWrapper mAdapterWrapper;
private boolean mSwipeItemMenuEnable = true;
private List<Integer> mDisableSwipeItemMenuList = new ArrayList<>();
public SwipeRecyclerView(Context context) {
this(context, null);
}
public SwipeRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SwipeRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
private void initializeItemTouchHelper() {
if (mItemTouchHelper == null) {
mItemTouchHelper = new DefaultItemTouchHelper();
mItemTouchHelper.attachToRecyclerView(this);
}
}
/**
* Set OnItemMoveListener.
*
* @param listener {@link OnItemMoveListener}.
*/
public void setOnItemMoveListener(OnItemMoveListener listener) {
initializeItemTouchHelper();
this.mItemTouchHelper.setOnItemMoveListener(listener);
}
/**
* Set OnItemMovementListener.
*
* @param listener {@link OnItemMovementListener}.
*/
public void setOnItemMovementListener(OnItemMovementListener listener) {
initializeItemTouchHelper();
this.mItemTouchHelper.setOnItemMovementListener(listener);
}
/**
* Set OnItemStateChangedListener.
*
* @param listener {@link OnItemStateChangedListener}.
*/
public void setOnItemStateChangedListener(OnItemStateChangedListener listener) {
initializeItemTouchHelper();
this.mItemTouchHelper.setOnItemStateChangedListener(listener);
}
/**
* Set the item menu to enable status.
*
* @param enabled true means available, otherwise not available; default is true.
*/
public void setSwipeItemMenuEnabled(boolean enabled) {
this.mSwipeItemMenuEnable = enabled;
}
/**
* True means available, otherwise not available; default is true.
*/
public boolean isSwipeItemMenuEnabled() {
return mSwipeItemMenuEnable;
}
/**
* Set the item menu to enable status.
*
* @param position the position of the item.
* @param enabled true means available, otherwise not available; default is true.
*/
public void setSwipeItemMenuEnabled(int position, boolean enabled) {
if (enabled) {
if (mDisableSwipeItemMenuList.contains(position)) {
mDisableSwipeItemMenuList.remove(Integer.valueOf(position));
}
} else {
if (!mDisableSwipeItemMenuList.contains(position)) {
mDisableSwipeItemMenuList.add(position);
}
}
}
/**
* True means available, otherwise not available; default is true.
*
* @param position the position of the item.
*/
public boolean isSwipeItemMenuEnabled(int position) {
return !mDisableSwipeItemMenuList.contains(position);
}
/**
* Set can long press drag.
*
* @param canDrag drag true, otherwise is can't.
*/
public void setLongPressDragEnabled(boolean canDrag) {
initializeItemTouchHelper();
this.mItemTouchHelper.setLongPressDragEnabled(canDrag);
}
/**
* Get can long press drag.
*
* @return drag true, otherwise is can't.
*/
public boolean isLongPressDragEnabled() {
initializeItemTouchHelper();
return this.mItemTouchHelper.isLongPressDragEnabled();
}
/**
* Set can swipe delete.
*
* @param canSwipe swipe true, otherwise is can't.
*/
public void setItemViewSwipeEnabled(boolean canSwipe) {
initializeItemTouchHelper();
allowSwipeDelete = canSwipe; // swipe and menu conflict.
this.mItemTouchHelper.setItemViewSwipeEnabled(canSwipe);
}
/**
* Get can long press swipe.
*
* @return swipe true, otherwise is can't.
*/
public boolean isItemViewSwipeEnabled() {
initializeItemTouchHelper();
return this.mItemTouchHelper.isItemViewSwipeEnabled();
}
/**
* Start drag a item.
*
* @param viewHolder the ViewHolder to start dragging. It must be a direct child of RecyclerView.
*/
public void startDrag(ViewHolder viewHolder) {
initializeItemTouchHelper();
this.mItemTouchHelper.startDrag(viewHolder);
}
/**
* Star swipe a item.
*
* @param viewHolder the ViewHolder to start swiping. It must be a direct child of RecyclerView.
*/
public void startSwipe(ViewHolder viewHolder) {
initializeItemTouchHelper();
this.mItemTouchHelper.startSwipe(viewHolder);
}
/**
* Check the Adapter and throw an exception if it already exists.
*/
private void checkAdapterExist(String message) {
if (mAdapterWrapper != null) throw new IllegalStateException(message);
}
/**
* Set item click listener.
*/
public void setOnItemClickListener(OnItemClickListener listener) {
if (listener == null) return;
checkAdapterExist("Cannot set item click listener, setAdapter has already been called.");
this.mOnItemClickListener = new ItemClickListener(this, listener);
}
private static class ItemClickListener implements OnItemClickListener {
private SwipeRecyclerView mRecyclerView;
private OnItemClickListener mListener;
public ItemClickListener(SwipeRecyclerView recyclerView, OnItemClickListener listener) {
this.mRecyclerView = recyclerView;
this.mListener = listener;
}
@Override
public void onItemClick(View itemView, int position) {
position -= mRecyclerView.getHeaderCount();
if (position >= 0) mListener.onItemClick(itemView, position);
}
}
/**
* Set item click listener.
*/
public void setOnItemLongClickListener(OnItemLongClickListener listener) {
if (listener == null) return;
checkAdapterExist("Cannot set item long click listener, setAdapter has already been called.");
this.mOnItemLongClickListener = new ItemLongClickListener(this, listener);
}
private static class ItemLongClickListener implements OnItemLongClickListener {
private SwipeRecyclerView mRecyclerView;
private OnItemLongClickListener mListener;
public ItemLongClickListener(SwipeRecyclerView recyclerView, OnItemLongClickListener listener) {
this.mRecyclerView = recyclerView;
this.mListener = listener;
}
@Override
public void onItemLongClick(View itemView, int position) {
position -= mRecyclerView.getHeaderCount();
if (position >= 0) mListener.onItemLongClick(itemView, position);
}
}
/**
* Set to create menu listener.
*/
public void setSwipeMenuCreator(SwipeMenuCreator menuCreator) {
if (menuCreator == null) return;
checkAdapterExist("Cannot set menu creator, setAdapter has already been called.");
this.mSwipeMenuCreator = menuCreator;
}
/**
* Set to click menu listener.
*/
public void setOnItemMenuClickListener(OnItemMenuClickListener listener) {
if (listener == null) return;
checkAdapterExist("Cannot set menu item click listener, setAdapter has already been called.");
this.mOnItemMenuClickListener = new ItemMenuClickListener(this, listener);
}
private static class ItemMenuClickListener implements OnItemMenuClickListener {
private SwipeRecyclerView mRecyclerView;
private OnItemMenuClickListener mListener;
public ItemMenuClickListener(SwipeRecyclerView recyclerView, OnItemMenuClickListener listener) {
this.mRecyclerView = recyclerView;
this.mListener = listener;
}
@Override
public void onItemClick(SwipeMenuBridge menuBridge, int position) {
position -= mRecyclerView.getHeaderCount();
if (position >= 0) {
mListener.onItemClick(menuBridge, position);
}
}
}
@Override
public void setLayoutManager(LayoutManager layoutManager) {
if (layoutManager instanceof GridLayoutManager) {
final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
final GridLayoutManager.SpanSizeLookup spanSizeLookupHolder = gridLayoutManager.getSpanSizeLookup();
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (mAdapterWrapper.isHeader(position) || mAdapterWrapper.isFooter(position)) {
return gridLayoutManager.getSpanCount();
}
if (spanSizeLookupHolder != null) {
return spanSizeLookupHolder.getSpanSize(position - getHeaderCount());
}
return 1;
}
});
}
super.setLayoutManager(layoutManager);
}
/**
* Get the original adapter.
*/
public Adapter getOriginAdapter() {
if (mAdapterWrapper == null) return null;
return mAdapterWrapper.getOriginAdapter();
}
@Override
public void setAdapter(Adapter adapter) {
if (mAdapterWrapper != null) {
mAdapterWrapper.getOriginAdapter().unregisterAdapterDataObserver(mAdapterDataObserver);
}
if (adapter == null) {
mAdapterWrapper = null;
} else {
adapter.registerAdapterDataObserver(mAdapterDataObserver);
mAdapterWrapper = new AdapterWrapper(getContext(), adapter);
mAdapterWrapper.setOnItemClickListener(mOnItemClickListener);
mAdapterWrapper.setOnItemLongClickListener(mOnItemLongClickListener);
mAdapterWrapper.setSwipeMenuCreator(mSwipeMenuCreator);
mAdapterWrapper.setOnItemMenuClickListener(mOnItemMenuClickListener);
if (mHeaderViewList.size() > 0) {
for (View view : mHeaderViewList) {
mAdapterWrapper.addHeaderView(view);
}
}
if (mFooterViewList.size() > 0) {
for (View view : mFooterViewList) {
mAdapterWrapper.addFooterView(view);
}
}
}
super.setAdapter(mAdapterWrapper);
}
private AdapterDataObserver mAdapterDataObserver = new AdapterDataObserver() {
@Override
public void onChanged() {
mAdapterWrapper.notifyDataSetChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
positionStart += getHeaderCount();
mAdapterWrapper.notifyItemRangeChanged(positionStart, itemCount);
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
positionStart += getHeaderCount();
mAdapterWrapper.notifyItemRangeChanged(positionStart, itemCount, payload);
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
positionStart += getHeaderCount();
mAdapterWrapper.notifyItemRangeInserted(positionStart, itemCount);
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
positionStart += getHeaderCount();
mAdapterWrapper.notifyItemRangeRemoved(positionStart, itemCount);
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
fromPosition += getHeaderCount();
toPosition += getHeaderCount();
mAdapterWrapper.notifyItemMoved(fromPosition, toPosition);
}
};
private List<View> mHeaderViewList = new ArrayList<>();
private List<View> mFooterViewList = new ArrayList<>();
/**
* Add view at the headers.
*/
public void addHeaderView(View view) {
mHeaderViewList.add(view);
if (mAdapterWrapper != null) {
mAdapterWrapper.addHeaderViewAndNotify(view);
}
}
/**
* Remove view from header.
*/
public void removeHeaderView(View view) {
mHeaderViewList.remove(view);
if (mAdapterWrapper != null) {
mAdapterWrapper.removeHeaderViewAndNotify(view);
}
}
/**
* Add view at the footer.
*/
public void addFooterView(View view) {
mFooterViewList.add(view);
if (mAdapterWrapper != null) {
mAdapterWrapper.addFooterViewAndNotify(view);
}
}
public void removeFooterView(View view) {
mFooterViewList.remove(view);
if (mAdapterWrapper != null) {
mAdapterWrapper.removeFooterViewAndNotify(view);
}
}
/**
* Get size of headers.
*/
public int getHeaderCount() {
if (mAdapterWrapper == null) return 0;
return mAdapterWrapper.getHeaderCount();
}
/**
* Get size of footer.
*/
public int getFooterCount() {
if (mAdapterWrapper == null) return 0;
return mAdapterWrapper.getFooterCount();
}
/**
* Get ViewType of item.
*/
public int getItemViewType(int position) {
if (mAdapterWrapper == null) return 0;
return mAdapterWrapper.getItemViewType(position);
}
/**
* open menu on left.
*
* @param position position.
*/
public void smoothOpenLeftMenu(int position) {
smoothOpenMenu(position, LEFT_DIRECTION, SwipeMenuLayout.DEFAULT_SCROLLER_DURATION);
}
/**
* open menu on left.
*
* @param position position.
* @param duration time millis.
*/
public void smoothOpenLeftMenu(int position, int duration) {
smoothOpenMenu(position, LEFT_DIRECTION, duration);
}
/**
* open menu on right.
*
* @param position position.
*/
public void smoothOpenRightMenu(int position) {
smoothOpenMenu(position, RIGHT_DIRECTION, SwipeMenuLayout.DEFAULT_SCROLLER_DURATION);
}
/**
* open menu on right.
*
* @param position position.
* @param duration time millis.
*/
public void smoothOpenRightMenu(int position, int duration) {
smoothOpenMenu(position, RIGHT_DIRECTION, duration);
}
/**
* open menu.
*
* @param position position.
* @param direction use {@link #LEFT_DIRECTION}, {@link #RIGHT_DIRECTION}.
* @param duration time millis.
*/
public void smoothOpenMenu(int position, @DirectionMode int direction, int duration) {
if (mOldSwipedLayout != null) {
if (mOldSwipedLayout.isMenuOpen()) {
mOldSwipedLayout.smoothCloseMenu();
}
}
position += getHeaderCount();
ViewHolder vh = findViewHolderForAdapterPosition(position);
if (vh != null) {
View itemView = getSwipeMenuView(vh.itemView);
if (itemView instanceof SwipeMenuLayout) {
mOldSwipedLayout = (SwipeMenuLayout) itemView;
if (direction == RIGHT_DIRECTION) {
mOldTouchedPosition = position;
mOldSwipedLayout.smoothOpenRightMenu(duration);
} else if (direction == LEFT_DIRECTION) {
mOldTouchedPosition = position;
mOldSwipedLayout.smoothOpenLeftMenu(duration);
}
}
}
}
/**
* Close menu.
*/
public void smoothCloseMenu() {
if (mOldSwipedLayout != null && mOldSwipedLayout.isMenuOpen()) {
mOldSwipedLayout.smoothCloseMenu();
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
boolean isIntercepted = super.onInterceptTouchEvent(e);
if (allowSwipeDelete || mSwipeMenuCreator == null) {
return isIntercepted;
} else {
if (e.getPointerCount() > 1) return true;
int action = e.getAction();
int x = (int) e.getX();
int y = (int) e.getY();
int touchPosition = getChildAdapterPosition(findChildViewUnder(x, y));
ViewHolder touchVH = findViewHolderForAdapterPosition(touchPosition);
SwipeMenuLayout touchView = null;
if (touchVH != null) {
View itemView = getSwipeMenuView(touchVH.itemView);
if (itemView instanceof SwipeMenuLayout) {
touchView = (SwipeMenuLayout) itemView;
}
}
boolean touchMenuEnable = mSwipeItemMenuEnable && !mDisableSwipeItemMenuList.contains(touchPosition);
if (touchView != null) {
touchView.setSwipeEnable(touchMenuEnable);
}
if (!touchMenuEnable) return isIntercepted;
switch (action) {
case MotionEvent.ACTION_DOWN: {
mDownX = x;
mDownY = y;
isIntercepted = false;
if (touchPosition != mOldTouchedPosition && mOldSwipedLayout != null &&
mOldSwipedLayout.isMenuOpen()) {
mOldSwipedLayout.smoothCloseMenu();
isIntercepted = true;
}
if (isIntercepted) {
mOldSwipedLayout = null;
mOldTouchedPosition = INVALID_POSITION;
} else if (touchView != null) {
mOldSwipedLayout = touchView;
mOldTouchedPosition = touchPosition;
}
break;
}
// They are sensitive to retain sliding and inertia.
case MotionEvent.ACTION_MOVE: {
isIntercepted = handleUnDown(x, y, isIntercepted);
if (mOldSwipedLayout == null) break;
ViewParent viewParent = getParent();
if (viewParent == null) break;
int disX = mDownX - x;
// 向左滑,显示右侧菜单,或者关闭左侧菜单。
boolean showRightCloseLeft = disX > 0 &&
(mOldSwipedLayout.hasRightMenu() || mOldSwipedLayout.isLeftCompleteOpen());
// 向右滑,显示左侧菜单,或者关闭右侧菜单。
boolean showLeftCloseRight = disX < 0 &&
(mOldSwipedLayout.hasLeftMenu() || mOldSwipedLayout.isRightCompleteOpen());
viewParent.requestDisallowInterceptTouchEvent(showRightCloseLeft || showLeftCloseRight);
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
isIntercepted = handleUnDown(x, y, isIntercepted);
break;
}
}
}
return isIntercepted;
}
private boolean handleUnDown(int x, int y, boolean defaultValue) {
int disX = mDownX - x;
int disY = mDownY - y;
// swipe
if (Math.abs(disX) > mScaleTouchSlop && Math.abs(disX) > Math.abs(disY)) return false;
// click
if (Math.abs(disY) < mScaleTouchSlop && Math.abs(disX) < mScaleTouchSlop) return false;
return defaultValue;
}
@Override
public boolean onTouchEvent(MotionEvent e) {
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if (mOldSwipedLayout != null && mOldSwipedLayout.isMenuOpen()) {
mOldSwipedLayout.smoothCloseMenu();
}
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return super.onTouchEvent(e);
}
private View getSwipeMenuView(View itemView) {
if (itemView instanceof SwipeMenuLayout) return itemView;
List<View> unvisited = new ArrayList<>();
unvisited.add(itemView);
while (!unvisited.isEmpty()) {
View child = unvisited.remove(0);
if (!(child instanceof ViewGroup)) { // view
continue;
}
if (child instanceof SwipeMenuLayout) return child;
ViewGroup group = (ViewGroup) child;
final int childCount = group.getChildCount();
for (int i = 0; i < childCount; i++) unvisited.add(group.getChildAt(i));
}
return itemView;
}
private int mScrollState = -1;
private boolean isLoadMore = false;
private boolean isAutoLoadMore = true;
private boolean isLoadError = false;
private boolean mDataEmpty = true;
private boolean mHasMore = false;
private LoadMoreView mLoadMoreView;
private LoadMoreListener mLoadMoreListener;
@Override
public void onScrollStateChanged(int state) {
this.mScrollState = state;
}
@Override
public void onScrolled(int dx, int dy) {
LayoutManager layoutManager = getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
int itemCount = layoutManager.getItemCount();
if (itemCount <= 0) return;
int lastVisiblePosition = linearLayoutManager.findLastVisibleItemPosition();
if (itemCount == lastVisiblePosition + 1 &&
(mScrollState == SCROLL_STATE_DRAGGING || mScrollState == SCROLL_STATE_SETTLING)) {
dispatchLoadMore();
}
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
int itemCount = layoutManager.getItemCount();
if (itemCount <= 0) return;
int[] lastVisiblePositionArray = staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(null);
int lastVisiblePosition = lastVisiblePositionArray[lastVisiblePositionArray.length - 1];
if (itemCount == lastVisiblePosition + 1 &&
(mScrollState == SCROLL_STATE_DRAGGING || mScrollState == SCROLL_STATE_SETTLING)) {
dispatchLoadMore();
}
}
}
private void dispatchLoadMore() {
if (isLoadError) return;
if (!isAutoLoadMore) {
if (mLoadMoreView != null) mLoadMoreView.onWaitToLoadMore(mLoadMoreListener);
} else {
if (isLoadMore || mDataEmpty || !mHasMore) return;
isLoadMore = true;
if (mLoadMoreView != null) mLoadMoreView.onLoading();
if (mLoadMoreListener != null) mLoadMoreListener.onLoadMore();
}
}
/**
* Use the default to load more View.
*/
public void useDefaultLoadMore() {
DefaultLoadMoreView defaultLoadMoreView = new DefaultLoadMoreView(getContext());
addFooterView(defaultLoadMoreView);
setLoadMoreView(defaultLoadMoreView);
}
/**
* Load more view.
*/
public void setLoadMoreView(LoadMoreView view) {
mLoadMoreView = view;
}
/**
* Load more listener.
*/
public void setLoadMoreListener(LoadMoreListener listener) {
mLoadMoreListener = listener;
}
/**
* Automatically load more automatically.
* <p>
* Non-auto-loading mode, you can to click on the item to load.
* </p>
*
* @param autoLoadMore you can use false.
* @see LoadMoreView#onWaitToLoadMore(LoadMoreListener)
*/
public void setAutoLoadMore(boolean autoLoadMore) {
isAutoLoadMore = autoLoadMore;
}
/**
* Load more done.
*
* @param dataEmpty data is empty ?
* @param hasMore has more data ?
*/
public final void loadMoreFinish(boolean dataEmpty, boolean hasMore) {
isLoadMore = false;
isLoadError = false;
mDataEmpty = dataEmpty;
mHasMore = hasMore;
if (mLoadMoreView != null) {
mLoadMoreView.onLoadFinish(dataEmpty, hasMore);
}
}
/**
* Called when data is loaded incorrectly.
*
* @param errorCode Error code, will be passed to the LoadView, you can according to it to customize the prompt
* information.
* @param errorMessage Error message.
*/
public void loadMoreError(int errorCode, String errorMessage) {
isLoadMore = false;
isLoadError = true;
if (mLoadMoreView != null) {
mLoadMoreView.onLoadError(errorCode, errorMessage);
}
}
public interface LoadMoreView {
/**
* Show progress.
*/
void onLoading();
/**
* Load finish, handle result.
*/
void onLoadFinish(boolean dataEmpty, boolean hasMore);
/**
* Non-auto-loading mode, you can to click on the item to load.
*/
void onWaitToLoadMore(LoadMoreListener loadMoreListener);
/**
* Load error.
*/
void onLoadError(int errorCode, String errorMessage);
}
public interface LoadMoreListener {
/**
* More data should be requested.
*/
void onLoadMore();
}
}

@ -0,0 +1,132 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview.touch;
import androidx.recyclerview.widget.ItemTouchHelper;
/**
* Created by Yolanda on 2016/4/19.
*/
public class DefaultItemTouchHelper extends ItemTouchHelper {
private ItemTouchHelperCallback mItemTouchHelperCallback;
/**
* Create default item touch helper.
*/
public DefaultItemTouchHelper() {
this(new ItemTouchHelperCallback());
}
/**
* @param callback the behavior of ItemTouchHelper.
*/
private DefaultItemTouchHelper(ItemTouchHelperCallback callback) {
super(callback);
mItemTouchHelperCallback = callback;
}
/**
* Set OnItemMoveListener.
*
* @param onItemMoveListener {@link OnItemMoveListener}.
*/
public void setOnItemMoveListener(OnItemMoveListener onItemMoveListener) {
mItemTouchHelperCallback.setOnItemMoveListener(onItemMoveListener);
}
/**
* Get OnItemMoveListener.
*
* @return {@link OnItemMoveListener}.
*/
public OnItemMoveListener getOnItemMoveListener() {
return mItemTouchHelperCallback.getOnItemMoveListener();
}
/**
* Set OnItemMovementListener.
*
* @param onItemMovementListener {@link OnItemMovementListener}.
*/
public void setOnItemMovementListener(OnItemMovementListener onItemMovementListener) {
mItemTouchHelperCallback.setOnItemMovementListener(onItemMovementListener);
}
/**
* Get OnItemMovementListener.
*
* @return {@link OnItemMovementListener}.
*/
public OnItemMovementListener getOnItemMovementListener() {
return mItemTouchHelperCallback.getOnItemMovementListener();
}
/**
* Set can long press drag.
*
* @param canDrag drag true, otherwise is can't.
*/
public void setLongPressDragEnabled(boolean canDrag) {
mItemTouchHelperCallback.setLongPressDragEnabled(canDrag);
}
/**
* Get can long press drag.
*
* @return drag true, otherwise is can't.
*/
public boolean isLongPressDragEnabled() {
return mItemTouchHelperCallback.isLongPressDragEnabled();
}
/**
* Set can long press swipe.
*
* @param canSwipe swipe true, otherwise is can't.
*/
public void setItemViewSwipeEnabled(boolean canSwipe) {
mItemTouchHelperCallback.setItemViewSwipeEnabled(canSwipe);
}
/**
* Get can long press swipe.
*
* @return swipe true, otherwise is can't.
*/
public boolean isItemViewSwipeEnabled() {
return this.mItemTouchHelperCallback.isItemViewSwipeEnabled();
}
/**
* Set OnItemStateChangedListener.
*
* @param onItemStateChangedListener {@link OnItemStateChangedListener}.
*/
public void setOnItemStateChangedListener(OnItemStateChangedListener onItemStateChangedListener) {
this.mItemTouchHelperCallback.setOnItemStateChangedListener(onItemStateChangedListener);
}
/**
* Get OnItemStateChangedListener.
*
* @return {@link OnItemStateChangedListener}.
*/
public OnItemStateChangedListener getOnItemStateChangedListener() {
return this.mItemTouchHelperCallback.getOnItemStateChangedListener();
}
}

@ -0,0 +1,174 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview.touch;
import android.graphics.Canvas;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by Yolanda on 2016/4/19.
*/
class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
private OnItemMovementListener onItemMovementListener;
private OnItemMoveListener onItemMoveListener;
private OnItemStateChangedListener onItemStateChangedListener;
private boolean isItemViewSwipeEnabled;
private boolean isLongPressDragEnabled;
public ItemTouchHelperCallback() {
}
public void setLongPressDragEnabled(boolean canDrag) {
this.isLongPressDragEnabled = canDrag;
}
@Override
public boolean isLongPressDragEnabled() {
return isLongPressDragEnabled;
}
public void setItemViewSwipeEnabled(boolean canSwipe) {
this.isItemViewSwipeEnabled = canSwipe;
}
@Override
public boolean isItemViewSwipeEnabled() {
return isItemViewSwipeEnabled;
}
public void setOnItemMoveListener(OnItemMoveListener onItemMoveListener) {
this.onItemMoveListener = onItemMoveListener;
}
public OnItemMoveListener getOnItemMoveListener() {
return onItemMoveListener;
}
public void setOnItemMovementListener(OnItemMovementListener onItemMovementListener) {
this.onItemMovementListener = onItemMovementListener;
}
public OnItemMovementListener getOnItemMovementListener() {
return onItemMovementListener;
}
public void setOnItemStateChangedListener(OnItemStateChangedListener onItemStateChangedListener) {
this.onItemStateChangedListener = onItemStateChangedListener;
}
public OnItemStateChangedListener getOnItemStateChangedListener() {
return onItemStateChangedListener;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder targetViewHolder) {
if (onItemMovementListener != null) {
int dragFlags = onItemMovementListener.onDragFlags(recyclerView, targetViewHolder);
int swipeFlags = onItemMovementListener.onSwipeFlags(recyclerView, targetViewHolder);
return makeMovementFlags(dragFlags, swipeFlags);
} else {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager)layoutManager;
if (linearLayoutManager.getOrientation() == LinearLayoutManager.HORIZONTAL) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT |
ItemTouchHelper.RIGHT;
int swipeFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
return makeMovementFlags(dragFlags, swipeFlags);
} else {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT |
ItemTouchHelper.RIGHT;
int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
return makeMovementFlags(dragFlags, swipeFlags);
}
} else if (layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager)layoutManager;
if (linearLayoutManager.getOrientation() == LinearLayoutManager.HORIZONTAL) {
int dragFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
int swipeFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
return makeMovementFlags(dragFlags, swipeFlags);
} else {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
return makeMovementFlags(dragFlags, swipeFlags);
}
}
}
return makeMovementFlags(0, 0);
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
// 判断当前是否是swipe方式:侧滑。
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
//1.ItemView--ViewHolder; 2.侧滑条目的透明度程度关联谁?dX(delta增量,范围:当前条目-width~width)。
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
float alpha = 1;
if (layoutManager instanceof LinearLayoutManager) {
int orientation = ((LinearLayoutManager)layoutManager).getOrientation();
if (orientation == LinearLayoutManager.HORIZONTAL) {
alpha = 1 - Math.abs(dY) / viewHolder.itemView.getHeight();
} else if (orientation == LinearLayoutManager.VERTICAL) {
alpha = 1 - Math.abs(dX) / viewHolder.itemView.getWidth();
}
}
viewHolder.itemView.setAlpha(alpha);//1~0
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
@Override
public boolean onMove(RecyclerView arg0, RecyclerView.ViewHolder srcHolder, RecyclerView.ViewHolder targetHolder) {
if (onItemMoveListener != null) {
// 回调刷新数据及界面。
return onItemMoveListener.onItemMove(srcHolder, targetHolder);
}
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
// 回调刷新数据及界面。
if (onItemMoveListener != null) onItemMoveListener.onItemDismiss(viewHolder);
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
if (onItemStateChangedListener != null && actionState != OnItemStateChangedListener.ACTION_STATE_IDLE) {
onItemStateChangedListener.onSelectedChanged(viewHolder, actionState);
}
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if (onItemStateChangedListener != null) {
onItemStateChangedListener.onSelectedChanged(viewHolder, OnItemStateChangedListener.ACTION_STATE_IDLE);
}
}
}

@ -0,0 +1,42 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview.touch;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by Yolanda on 2016/4/19.
*/
public interface OnItemMoveListener {
/**
* When drag and drop the callback.
*
* @param srcHolder src.
* @param targetHolder target.
*
* @return To deal with the returns true, false otherwise.
*/
boolean onItemMove(RecyclerView.ViewHolder srcHolder, RecyclerView.ViewHolder targetHolder);
/**
* When items should be removed when the callback.
*
* @param srcHolder src.
*/
void onItemDismiss(RecyclerView.ViewHolder srcHolder);
}

@ -0,0 +1,56 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview.touch;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by Yan Zhenjie on 2016/8/1.
*/
public interface OnItemMovementListener {
int INVALID = 0;
int LEFT = ItemTouchHelper.LEFT;
int UP = ItemTouchHelper.UP;
int RIGHT = ItemTouchHelper.RIGHT;
int DOWN = ItemTouchHelper.DOWN;
/**
* Can drag and drop the ViewHolder?
*
* @param recyclerView {@link RecyclerView}.
* @param targetViewHolder target ViewHolder.
*
* @return use {@link #LEFT}, {@link #UP}, {@link #RIGHT}, {@link #DOWN}.
*/
int onDragFlags(RecyclerView recyclerView, RecyclerView.ViewHolder targetViewHolder);
/**
* Can swipe and drop the ViewHolder?
*
* @param recyclerView {@link RecyclerView}.
* @param targetViewHolder target ViewHolder.
*
* @return use {@link #LEFT}, {@link #UP}, {@link #RIGHT}, {@link #DOWN}.
*/
int onSwipeFlags(RecyclerView recyclerView, RecyclerView.ViewHolder targetViewHolder);
}

@ -0,0 +1,50 @@
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview.touch;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by Yan Zhenjie on 2016/8/12.
*/
public interface OnItemStateChangedListener {
/**
* ItemTouchHelper is in idle state. At this state, either there is no related motion event by the user or latest
* motion events have not yet triggered a swipe or drag.
*/
int ACTION_STATE_IDLE = ItemTouchHelper.ACTION_STATE_IDLE;
/**
* A View is currently being swiped.
*/
int ACTION_STATE_SWIPE = ItemTouchHelper.ACTION_STATE_SWIPE;
/**
* A View is currently being dragged.
*/
int ACTION_STATE_DRAG = ItemTouchHelper.ACTION_STATE_DRAG;
/**
* Called when the ViewHolder swiped or dragged by the ItemTouchHelper is changed.
*
* @param viewHolder The new ViewHolder that is being swiped or dragged. Might be null if it is cleared.
* @param actionState One of {@link OnItemStateChangedListener#ACTION_STATE_IDLE}, {@link
* OnItemStateChangedListener#ACTION_STATE_SWIPE} or {@link OnItemStateChangedListener#ACTION_STATE_DRAG}.
*/
void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState);
}

@ -0,0 +1,74 @@
/*
* Copyright 2019 Zhenjie Yan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview.widget;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.View;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by Zhenjie Yan on 1/30/19.
*/
public class BorderItemDecoration extends RecyclerView.ItemDecoration {
private final int mWidth;
private final int mHeight;
private final Drawer mDrawer;
/**
* @param color divider line color.
*/
public BorderItemDecoration(@ColorInt int color) {
this(color, 4, 4);
}
/**
* @param color line color.
* @param width line width.
* @param height line height.
*/
public BorderItemDecoration(@ColorInt int color, int width, int height) {
this.mWidth = Math.round(width / 2F);
this.mHeight = Math.round(height / 2F);
this.mDrawer = new ColorDrawer(color, mWidth, mHeight);
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent,
@NonNull RecyclerView.State state) {
outRect.set(mWidth, mHeight, mWidth, mHeight);
}
@Override
public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
canvas.save();
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
assert layoutManager != null;
int childCount = layoutManager.getChildCount();
for (int i = 0; i < childCount; i++) {
final View view = layoutManager.getChildAt(i);
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
}
canvas.restore();
}
}

@ -0,0 +1,47 @@
/*
* Copyright 2018 Yan Zhenjie.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview.widget;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import androidx.annotation.ColorInt;
/**
* Created by YanZhenjie on 2018/4/20.
*/
public class ColorDrawer extends Drawer {
public ColorDrawer(int color, int width, int height) {
super(new ColorDrawable(opaqueColor(color)), width, height);
}
/**
* The target color is packaged in an opaque color.
*
* @param color color.
* @return color.
*/
@ColorInt
public static int opaqueColor(@ColorInt int color) {
int alpha = Color.alpha(color);
if (alpha == 0) return color;
int red = Color.red(color);
int green = Color.green(color);
int blue = Color.blue(color);
return Color.argb(255, red, green, blue);
}
}

@ -0,0 +1,380 @@
/*
* Copyright 2017 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview.widget;
import android.graphics.Canvas;
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.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
/**
* Created by YanZhenjie on 2017/8/14.
*/
public class DefaultItemDecoration extends RecyclerView.ItemDecoration {
private final int mWidth;
private final int mHeight;
private final Drawer mDrawer;
/**
* @param color divider line color.
*/
public DefaultItemDecoration(@ColorInt int color) {
this(color, 4, 4);
}
/**
* @param color line color.
* @param width line width.
* @param height line height.
*/
public DefaultItemDecoration(@ColorInt int color, int width, int height) {
this.mWidth = Math.round(width / 2F);
this.mHeight = Math.round(height / 2F);
this.mDrawer = new ColorDrawer(color, mWidth, mHeight);
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent,
@NonNull RecyclerView.State state) {
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
int orientation = getOrientation(layoutManager);
int position = parent.getChildLayoutPosition(view);
int spanCount = getSpanCount(layoutManager);
int childCount = layoutManager.getItemCount();
if (orientation == RecyclerView.VERTICAL) {
offsetVertical(outRect, position, spanCount, childCount);
} else {
offsetHorizontal(outRect, position, spanCount, childCount);
}
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
outRect.set(mWidth, mHeight, mWidth, mHeight); // |-|-
}
}
private void offsetHorizontal(Rect outRect, int position, int spanCount, int childCount) {
boolean firstRaw = isFirstRaw(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean lastRaw = isLastRaw(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean firstColumn = isFirstColumn(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean lastColumn = isLastColumn(RecyclerView.HORIZONTAL, position, spanCount, childCount);
if (spanCount == 1) {
if (firstColumn && lastColumn) { // xxxx
outRect.set(0, 0, 0, 0);
} else if (firstColumn) { // xx|x
outRect.set(0, 0, mWidth, 0);
} else if (lastColumn) { // |xxx
outRect.set(mWidth, 0, 0, 0);
} else { // |x|x
outRect.set(mWidth, 0, mWidth, 0);
}
} else {
if (firstColumn && firstRaw) { // xx|-
outRect.set(0, 0, mWidth, mHeight);
} else if (firstColumn && lastRaw) { // x-|x
outRect.set(0, mHeight, mWidth, 0);
} else if (lastColumn && firstRaw) { // |xx-
outRect.set(mWidth, 0, 0, mHeight);
} else if (lastColumn && lastRaw) { // |-xx
outRect.set(mWidth, mHeight, 0, 0);
} else if (firstColumn) { // x-|-
outRect.set(0, mHeight, mWidth, mHeight);
} else if (lastColumn) { // |-x-
outRect.set(mWidth, mHeight, 0, mHeight);
} else if (firstRaw) { // |x|-
outRect.set(mWidth, 0, mWidth, mHeight);
} else if (lastRaw) { // |-|x
outRect.set(mWidth, mHeight, mWidth, 0);
} else { // |-|-
outRect.set(mWidth, mHeight, mWidth, mHeight);
}
}
}
private void offsetVertical(Rect outRect, int position, int spanCount, int childCount) {
boolean firstRaw = isFirstRaw(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean lastRaw = isLastRaw(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean firstColumn = isFirstColumn(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean lastColumn = isLastColumn(RecyclerView.VERTICAL, position, spanCount, childCount);
if (spanCount == 1) {
if (firstRaw && lastRaw) { // xxxx
outRect.set(0, 0, 0, 0);
} else if (firstRaw) { // xxx-
outRect.set(0, 0, 0, mHeight);
} else if (lastRaw) { // x-xx
outRect.set(0, mHeight, 0, 0);
} else { // x-x-
outRect.set(0, mHeight, 0, mHeight);
}
} else {
if (firstRaw && firstColumn) { // xx|-
outRect.set(0, 0, mWidth, mHeight);
} else if (firstRaw && lastColumn) { // |xx-
outRect.set(mWidth, 0, 0, mHeight);
} else if (lastRaw && firstColumn) { // x-|x
outRect.set(0, mHeight, mWidth, 0);
} else if (lastRaw && lastColumn) { // |-xx
outRect.set(mWidth, mHeight, 0, 0);
} else if (firstRaw) { // |x|-
outRect.set(mWidth, 0, mWidth, mHeight);
} else if (lastRaw) { // |-|x
outRect.set(mWidth, mHeight, mWidth, 0);
} else if (firstColumn) { // x-|-
outRect.set(0, mHeight, mWidth, mHeight);
} else if (lastColumn) { // |-x-
outRect.set(mWidth, mHeight, 0, mHeight);
} else { // |-|-
outRect.set(mWidth, mHeight, mWidth, mHeight);
}
}
}
private int getOrientation(RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof LinearLayoutManager) {
return ((LinearLayoutManager)layoutManager).getOrientation();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
return ((StaggeredGridLayoutManager)layoutManager).getOrientation();
}
return RecyclerView.VERTICAL;
}
private int getSpanCount(RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof GridLayoutManager) {
return ((GridLayoutManager)layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
return ((StaggeredGridLayoutManager)layoutManager).getSpanCount();
}
return 1;
}
private boolean isFirstRaw(int orientation, int position, int columnCount, int childCount) {
if (orientation == RecyclerView.VERTICAL) {
return position < columnCount;
} else {
if (columnCount == 1) return true;
return position % columnCount == 0;
}
}
private boolean isLastRaw(int orientation, int position, int columnCount, int childCount) {
if (orientation == RecyclerView.VERTICAL) {
if (columnCount == 1) {
return position + 1 == childCount;
} else {
int lastRawItemCount = childCount % columnCount;
int rawCount = (childCount - lastRawItemCount) / columnCount + (lastRawItemCount > 0 ? 1 : 0);
int rawPositionJudge = (position + 1) % columnCount;
if (rawPositionJudge == 0) {
int positionRaw = (position + 1) / columnCount;
return rawCount == positionRaw;
} else {
int rawPosition = (position + 1 - rawPositionJudge) / columnCount + 1;
return rawCount == rawPosition;
}
}
} else {
if (columnCount == 1) return true;
return (position + 1) % columnCount == 0;
}
}
private boolean isFirstColumn(int orientation, int position, int columnCount, int childCount) {
if (orientation == RecyclerView.VERTICAL) {
if (columnCount == 1) return true;
return position % columnCount == 0;
} else {
return position < columnCount;
}
}
private boolean isLastColumn(int orientation, int position, int columnCount, int childCount) {
if (orientation == RecyclerView.VERTICAL) {
if (columnCount == 1) return true;
return (position + 1) % columnCount == 0;
} else {
if (columnCount == 1) {
return position + 1 == childCount;
} else {
int lastRawItemCount = childCount % columnCount;
int rawCount = (childCount - lastRawItemCount) / columnCount + (lastRawItemCount > 0 ? 1 : 0);
int rawPositionJudge = (position + 1) % columnCount;
if (rawPositionJudge == 0) {
int positionRaw = (position + 1) / columnCount;
return rawCount == positionRaw;
} else {
int rawPosition = (position + 1 - rawPositionJudge) / columnCount + 1;
return rawCount == rawPosition;
}
}
}
}
@Override
public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
assert layoutManager != null;
int orientation = getOrientation(layoutManager);
int spanCount = getSpanCount(layoutManager);
int childCount = layoutManager.getChildCount();
if (layoutManager instanceof LinearLayoutManager) {
canvas.save();
for (int i = 0; i < childCount; i++) {
View view = layoutManager.getChildAt(i);
assert view != null;
int position = parent.getChildLayoutPosition(view);
if (orientation == RecyclerView.VERTICAL) {
drawVertical(canvas, view, position, spanCount, childCount);
} else {
drawHorizontal(canvas, view, position, spanCount, childCount);
}
}
canvas.restore();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
canvas.save();
for (int i = 0; i < childCount; i++) {
View view = layoutManager.getChildAt(i);
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
}
canvas.restore();
}
}
private void drawHorizontal(Canvas canvas, View view, int position, int spanCount, int childCount) {
boolean firstRaw = isFirstRaw(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean lastRaw = isLastRaw(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean firstColumn = isFirstColumn(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean lastColumn = isLastColumn(RecyclerView.HORIZONTAL, position, spanCount, childCount);
if (spanCount == 1) {
if (firstRaw && lastColumn) { // xxxx
// Nothing.
} else if (firstColumn) { // xx|x
mDrawer.drawRight(view, canvas);
} else if (lastColumn) { // |xxx
mDrawer.drawLeft(view, canvas);
} else { // |x|x
mDrawer.drawLeft(view, canvas);
mDrawer.drawRight(view, canvas);
}
} else {
if (firstColumn && firstRaw) { // xx|-
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (firstColumn && lastRaw) { // x-|x
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
} else if (lastColumn && firstRaw) { // |xx-
mDrawer.drawLeft(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastColumn && lastRaw) { // |-xx
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
} else if (firstColumn) { // x-|-
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastColumn) { // |-x-
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (firstRaw) { // |x|-
mDrawer.drawLeft(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastRaw) { // |-|x
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
} else { // |-|-
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
}
}
}
private void drawVertical(Canvas canvas, View view, int position, int spanCount, int childCount) {
boolean firstRaw = isFirstRaw(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean lastRaw = isLastRaw(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean firstColumn = isFirstColumn(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean lastColumn = isLastColumn(RecyclerView.VERTICAL, position, spanCount, childCount);
if (spanCount == 1) {
if (firstRaw && lastRaw) { // xxxx
// Nothing.
} else if (firstRaw) { // xxx-
mDrawer.drawBottom(view, canvas);
} else if (lastRaw) { // x-xx
mDrawer.drawTop(view, canvas);
} else { // x-x-
mDrawer.drawTop(view, canvas);
mDrawer.drawBottom(view, canvas);
}
} else {
if (firstRaw && firstColumn) { // xx|-
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (firstRaw && lastColumn) { // |xx-
mDrawer.drawLeft(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastRaw && firstColumn) { // x-|x
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
} else if (lastRaw && lastColumn) { // |-xx
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
} else if (firstRaw) { // |x|-
mDrawer.drawLeft(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastRaw) { // |-|x
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
} else if (firstColumn) { // x-|-
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastColumn) { // |-x-
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawBottom(view, canvas);
} else { // |-|-
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
}
}
}
}

@ -0,0 +1,116 @@
/*
* Copyright 2017 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview.widget;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.project.survey.R;
import com.project.survey.widget.swiperecyclerview.SwipeRecyclerView;
/**
* Created by YanZhenjie on 2017/7/21.
*/
public class DefaultLoadMoreView extends LinearLayout implements SwipeRecyclerView.LoadMoreView, View.OnClickListener {
private ProgressBar mProgressBar;
private TextView mTvMessage;
private SwipeRecyclerView.LoadMoreListener mLoadMoreListener;
public DefaultLoadMoreView(Context context) {
this(context, null);
}
public DefaultLoadMoreView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setLayoutParams(new ViewGroup.LayoutParams(-1, -2));
setGravity(Gravity.CENTER);
setVisibility(GONE);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
int minHeight = (int) (displayMetrics.density * 60 + 0.5);
setMinimumHeight(minHeight);
inflate(getContext(), R.layout.x_recycler_view_load_more, this);
mProgressBar = findViewById(R.id.progress_bar);
mTvMessage = findViewById(R.id.tv_load_more_message);
setOnClickListener(this);
}
@Override
public void onLoading() {
setVisibility(VISIBLE);
mProgressBar.setVisibility(VISIBLE);
mTvMessage.setVisibility(VISIBLE);
mTvMessage.setText(R.string.x_recycler_load_more_message);
}
@Override
public void onLoadFinish(boolean dataEmpty, boolean hasMore) {
if (!hasMore) {
setVisibility(VISIBLE);
if (dataEmpty) {
mProgressBar.setVisibility(GONE);
mTvMessage.setVisibility(VISIBLE);
mTvMessage.setText(R.string.x_recycler_data_empty);
} else {
mProgressBar.setVisibility(GONE);
mTvMessage.setVisibility(VISIBLE);
mTvMessage.setText(R.string.x_recycler_more_not);
}
} else {
setVisibility(INVISIBLE);
}
}
@Override
public void onWaitToLoadMore(SwipeRecyclerView.LoadMoreListener loadMoreListener) {
this.mLoadMoreListener = loadMoreListener;
setVisibility(VISIBLE);
mProgressBar.setVisibility(GONE);
mTvMessage.setVisibility(VISIBLE);
mTvMessage.setText(R.string.x_recycler_click_load_more);
}
@Override
public void onLoadError(int errorCode, String errorMessage) {
setVisibility(VISIBLE);
mProgressBar.setVisibility(GONE);
mTvMessage.setVisibility(VISIBLE);
mTvMessage.setText(
TextUtils.isEmpty(errorMessage) ? getContext().getString(R.string.x_recycler_load_error) : errorMessage);
}
@Override
public void onClick(View v) {
if (mLoadMoreListener != null) mLoadMoreListener.onLoadMore();
}
}

@ -0,0 +1,84 @@
/*
* Copyright 2018 Yan Zhenjie.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview.widget;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.view.View;
/**
* Created by YanZhenjie on 2018/4/20.
*/
public class Drawer {
private final Drawable mDivider;
private final int mWidth;
private final int mHeight;
public Drawer(Drawable divider, int width, int height) {
this.mDivider = divider;
this.mWidth = width;
this.mHeight = height;
}
/**
* Draw the divider on the left side of the Item.
*/
public void drawLeft(View view, Canvas c) {
int left = view.getLeft() - mWidth;
int top = view.getTop() - mHeight;
int right = left + mWidth;
int bottom = view.getBottom() + mHeight;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
/**
* Draw the divider on the top side of the Item.
*/
public void drawTop(View view, Canvas c) {
int left = view.getLeft() - mWidth;
int top = view.getTop() - mHeight;
int right = view.getRight() + mWidth;
int bottom = top + mHeight;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
/**
* Draw the divider on the top side of the Item.
*/
public void drawRight(View view, Canvas c) {
int left = view.getRight();
int top = view.getTop() - mHeight;
int right = left + mWidth;
int bottom = view.getBottom() + mHeight;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
/**
* Draw the divider on the top side of the Item.
*/
public void drawBottom(View view, Canvas c) {
int left = view.getLeft() - mWidth;
int top = view.getBottom();
int right = view.getRight() + mWidth;
int bottom = top + mHeight;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}

@ -0,0 +1,406 @@
/*
* Copyright 2017 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.project.survey.widget.swiperecyclerview.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import androidx.core.widget.NestedScrollView;
import java.util.ArrayList;
import java.util.List;
/**
* Created by YanZhenjie on 2017/7/20.
*/
public class StickyNestedScrollView extends NestedScrollView {
public interface OnViewStickyListener {
void onSticky(View view);
void onUnSticky(View view);
}
/**
* Tag for views that should stick and have constant drawing. e.g. TextViews, ImageViews etc
*/
public static final String STICKY_TAG = "sticky";
/**
* Flag for views that should stick and have non-constant drawing. e.g. Buttons, ProgressBars etc
*/
public static final String FLAG_NONCONSTANT = "-nonconstant";
/**
* Flag for views that have aren't fully opaque
*/
public static final String FLAG_HASTRANSPARENCY = "-hastransparency";
/**
* Default height of the shadow peeking out below the stuck view.
*/
private static final int DEFAULT_SHADOW_HEIGHT = 10; // dp;
private ArrayList<View> stickyViews;
private View currentlyStickingView;
private float stickyViewTopOffset;
private final Runnable invalidateRunnable = new Runnable() {
@Override
public void run() {
if (currentlyStickingView != null) {
int l = getLeftForViewRelativeOnlyChild(currentlyStickingView);
int t = getBottomForViewRelativeOnlyChild(currentlyStickingView);
int r = getRightForViewRelativeOnlyChild(currentlyStickingView);
int b = (int)(getScrollY() + (currentlyStickingView.getHeight() + stickyViewTopOffset));
invalidate(l, t, r, b);
}
postDelayed(this, 16);
}
};
private int stickyViewLeftOffset;
private boolean redirectTouchesToStickyView;
private boolean clippingToPadding;
private boolean clipToPaddingHasBeenSet;
private int mShadowHeight = DEFAULT_SHADOW_HEIGHT;
private Drawable mShadowDrawable;
private boolean hasNotDoneActionDown = true;
private List<OnViewStickyListener> mOnViewStickyListeners;
public StickyNestedScrollView(Context context) {
this(context, null);
}
public StickyNestedScrollView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.scrollViewStyle);
}
public StickyNestedScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setup();
}
public void addOnViewStickyListener(OnViewStickyListener stickyListener) {
if (mOnViewStickyListeners == null) mOnViewStickyListeners = new ArrayList<>();
mOnViewStickyListeners.add(stickyListener);
}
public void removeOnViewStickyListener(OnViewStickyListener stickyListener) {
if (mOnViewStickyListeners != null) mOnViewStickyListeners.remove(stickyListener);
}
public void clearOnViewStickyListener() {
if (mOnViewStickyListeners != null) mOnViewStickyListeners.clear();
}
public void setShadowHeight(int height) {
mShadowHeight = height;
}
public void setShadowDrawable(Drawable shadowDrawable) {
mShadowDrawable = shadowDrawable;
}
public void setup() {
stickyViews = new ArrayList<>();
}
private int getLeftForViewRelativeOnlyChild(View v) {
int left = v.getLeft();
while (v.getParent() != null && v.getParent() != getChildAt(0)) {
v = (View)v.getParent();
left += v.getLeft();
}
return left;
}
private int getTopForViewRelativeOnlyChild(View v) {
int top = v.getTop();
while (v.getParent() != null && v.getParent() != getChildAt(0)) {
v = (View)v.getParent();
top += v.getTop();
}
return top;
}
private int getRightForViewRelativeOnlyChild(View v) {
int right = v.getRight();
while (v.getParent() != null && v.getParent() != getChildAt(0)) {
v = (View)v.getParent();
right += v.getRight();
}
return right;
}
private int getBottomForViewRelativeOnlyChild(View v) {
int bottom = v.getBottom();
while (v.getParent() != null && v.getParent() != getChildAt(0)) {
v = (View)v.getParent();
bottom += v.getBottom();
}
return bottom;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (!clipToPaddingHasBeenSet) {
clippingToPadding = true;
}
notifyHierarchyChanged();
}
@Override
public void setClipToPadding(boolean clipToPadding) {
super.setClipToPadding(clipToPadding);
clippingToPadding = clipToPadding;
clipToPaddingHasBeenSet = true;
}
@Override
public void addView(View child) {
super.addView(child);
findStickyViews(child);
}
@Override
public void addView(View child, int index) {
super.addView(child, index);
findStickyViews(child);
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
super.addView(child, index, params);
findStickyViews(child);
}
@Override
public void addView(View child, int width, int height) {
super.addView(child, width, height);
findStickyViews(child);
}
@Override
public void addView(View child, ViewGroup.LayoutParams params) {
super.addView(child, params);
findStickyViews(child);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (currentlyStickingView != null) {
canvas.save();
canvas.translate(getPaddingLeft() + stickyViewLeftOffset,
getScrollY() + stickyViewTopOffset + (clippingToPadding ? getPaddingTop() : 0));
canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0), getWidth() - stickyViewLeftOffset,
currentlyStickingView.getHeight() + mShadowHeight + 1);
if (mShadowDrawable != null) {
int left = 0;
int top = currentlyStickingView.getHeight();
int right = currentlyStickingView.getWidth();
int bottom = currentlyStickingView.getHeight() + mShadowHeight;
mShadowDrawable.setBounds(left, top, right, bottom);
mShadowDrawable.draw(canvas);
}
canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0), getWidth(),
currentlyStickingView.getHeight());
if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARENCY)) {
showView(currentlyStickingView);
currentlyStickingView.draw(canvas);
hideView(currentlyStickingView);
} else {
currentlyStickingView.draw(canvas);
}
canvas.restore();
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
redirectTouchesToStickyView = true;
}
if (redirectTouchesToStickyView) {
redirectTouchesToStickyView = currentlyStickingView != null;
if (redirectTouchesToStickyView) {
redirectTouchesToStickyView = ev.getY() <= (currentlyStickingView.getHeight() + stickyViewTopOffset) &&
ev.getX() >= getLeftForViewRelativeOnlyChild(currentlyStickingView) &&
ev.getX() <= getRightForViewRelativeOnlyChild(currentlyStickingView);
}
} else if (currentlyStickingView == null) {
redirectTouchesToStickyView = false;
}
if (redirectTouchesToStickyView) {
ev.offsetLocation(0,
-1 * ((getScrollY() + stickyViewTopOffset) - getTopForViewRelativeOnlyChild(currentlyStickingView)));
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (redirectTouchesToStickyView) {
ev.offsetLocation(0,
((getScrollY() + stickyViewTopOffset) - getTopForViewRelativeOnlyChild(currentlyStickingView)));
}
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
hasNotDoneActionDown = false;
}
if (hasNotDoneActionDown) {
MotionEvent down = MotionEvent.obtain(ev);
down.setAction(MotionEvent.ACTION_DOWN);
super.onTouchEvent(down);
hasNotDoneActionDown = false;
}
if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) {
hasNotDoneActionDown = true;
}
return super.onTouchEvent(ev);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
doTheStickyThing();
}
private void doTheStickyThing() {
View viewThatShouldStick = null;
View approachingView = null;
for (View v : stickyViews) {
int viewTop = getTopForViewRelativeOnlyChild(v) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop());
if (viewTop <= 0) {
if (viewThatShouldStick == null || viewTop >
(getTopForViewRelativeOnlyChild(viewThatShouldStick) - getScrollY() +
(clippingToPadding ? 0 : getPaddingTop()))) {
viewThatShouldStick = v;
}
} else {
if (approachingView == null || viewTop <
(getTopForViewRelativeOnlyChild(approachingView) - getScrollY() +
(clippingToPadding ? 0 : getPaddingTop()))) {
approachingView = v;
}
}
}
if (viewThatShouldStick != null) {
stickyViewTopOffset = approachingView == null
? 0
: Math.min(0, getTopForViewRelativeOnlyChild(approachingView) - getScrollY() +
(clippingToPadding ? 0 : getPaddingTop()) - viewThatShouldStick.getHeight());
if (viewThatShouldStick != currentlyStickingView) {
if (currentlyStickingView != null) {
if (mOnViewStickyListeners != null) {
for (OnViewStickyListener onViewStickyListener : mOnViewStickyListeners)
onViewStickyListener.onUnSticky(currentlyStickingView);
}
stopStickingCurrentlyStickingView();
}
// only compute the left offset when we start sticking.
stickyViewLeftOffset = getLeftForViewRelativeOnlyChild(viewThatShouldStick);
startStickingView(viewThatShouldStick);
if (mOnViewStickyListeners != null) {
for (OnViewStickyListener onViewStickyListener : mOnViewStickyListeners)
onViewStickyListener.onSticky(currentlyStickingView);
}
}
} else if (currentlyStickingView != null) {
if (mOnViewStickyListeners != null) {
for (OnViewStickyListener onViewStickyListener : mOnViewStickyListeners)
onViewStickyListener.onUnSticky(currentlyStickingView);
}
stopStickingCurrentlyStickingView();
}
}
private void startStickingView(View viewThatShouldStick) {
currentlyStickingView = viewThatShouldStick;
if (currentlyStickingView != null) {
if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARENCY)) {
hideView(currentlyStickingView);
}
if (getStringTagForView(currentlyStickingView).contains(FLAG_NONCONSTANT)) {
post(invalidateRunnable);
}
}
}
private void stopStickingCurrentlyStickingView() {
if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARENCY)) {
showView(currentlyStickingView);
}
currentlyStickingView = null;
removeCallbacks(invalidateRunnable);
}
@Override
protected void onDetachedFromWindow() {
removeCallbacks(invalidateRunnable);
super.onDetachedFromWindow();
}
/**
* Notify that the sticky attribute has been added or removed from one or more views in the View hierarchy
*/
public void notifyStickyAttributeChanged() {
notifyHierarchyChanged();
}
private void notifyHierarchyChanged() {
if (currentlyStickingView != null) {
stopStickingCurrentlyStickingView();
}
stickyViews.clear();
findStickyViews(getChildAt(0));
doTheStickyThing();
invalidate();
}
private void findStickyViews(View v) {
if (!detainStickyView(v) && (v instanceof ViewGroup)) {
ViewGroup vg = (ViewGroup)v;
for (int i = 0; i < vg.getChildCount(); i++)
findStickyViews(vg.getChildAt(i));
}
}
private boolean detainStickyView(View view) {
String tag = getStringTagForView(view);
if (tag.contains(STICKY_TAG)) {
stickyViews.add(view);
return true;
}
return false;
}
private String getStringTagForView(View v) {
Object tagObject = v.getTag();
return String.valueOf(tagObject);
}
private void hideView(View v) {
v.setAlpha(0);
}
private void showView(View v) {
v.setAlpha(1);
}
}

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="99dp" />
<solid android:color="#f2f4f5" />
</shape>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="270"
android:endColor="#ffffff"
android:startColor="#1A000000" />
</shape>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16.5dp"
android:height="16.5dp"
android:viewportWidth="16.5"
android:viewportHeight="16.5">
<path
android:pathData="M0,0L7.333,0L7.333,9.167L0,9.167L0,0ZM9.167,0L16.5,0L16.5,5.5L9.167,5.5L9.167,0ZM5.5,7.333L5.5,1.833L1.833,1.833L1.833,7.333L5.5,7.333ZM14.667,3.667L14.667,1.833L11,1.833L11,3.667L14.667,3.667ZM9.167,7.333L16.5,7.333L16.5,16.5L9.167,16.5L9.167,7.333ZM14.667,14.667L14.667,9.167L11,9.167L11,14.667L14.667,14.667ZM0,11L7.333,11L7.333,16.5L0,16.5L0,11ZM5.5,14.667L5.5,12.833L1.833,12.833L1.833,14.667L5.5,14.667Z"
android:fillColor="#5B5A5E"
android:fillType="evenOdd"/>
</vector>

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="30dp"
android:height="30dp"
android:viewportWidth="30"
android:viewportHeight="30">
<path
android:pathData="M0,15a15,15 0,1 0,30 0a15,15 0,1 0,-30 0z"
android:fillColor="#E9F1FC"/>
<path
android:pathData="M10.5,8L19.5,8C20.325,8 21,8.63 21,9.4L21,20.6C21,21.37 20.325,22 19.5,22L10.5,22C9.675,22 9,21.37 9,20.6L9,9.4C9,8.63 9.675,8 10.5,8ZM14.25,9.4L10.5,9.4L10.5,15L12.375,13.95L14.25,15L14.25,9.4Z"
android:fillColor="#396BD0"
android:fillType="evenOdd"/>
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18.333dp"
android:height="18.333dp"
android:viewportWidth="18.333"
android:viewportHeight="18.333">
<path
android:pathData="M9.167,0C4.125,0 0,4.125 0,9.167C0,14.208 4.125,18.333 9.167,18.333C14.208,18.333 18.333,14.208 18.333,9.167C18.333,4.125 14.208,0 9.167,0ZM1.833,9.167C1.833,13.209 5.124,16.5 9.167,16.5C13.209,16.5 16.5,13.209 16.5,9.167C16.5,5.124 13.209,1.833 9.167,1.833C5.124,1.833 1.833,5.124 1.833,9.167ZM9.625,4.583L8.25,4.583L8.25,10.083L13.017,13.017L13.75,11.825L9.625,9.35L9.625,4.583Z"
android:fillColor="#396BD0"
android:fillType="evenOdd"/>
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="13.117dp"
android:height="13.117dp"
android:viewportWidth="13.117"
android:viewportHeight="13.117">
<path
android:pathData="M8.783,8.25L9.375,8.25L13.118,12L12,13.118L8.25,9.375L8.25,8.783L8.047,8.573C7.193,9.307 6.082,9.75 4.875,9.75C2.182,9.75 0,7.568 0,4.875C0,2.182 2.182,0 4.875,0C7.568,0 9.75,2.182 9.75,4.875C9.75,6.082 9.307,7.193 8.573,8.047L8.783,8.25ZM1.5,4.875C1.5,6.742 3.007,8.25 4.875,8.25C6.742,8.25 8.25,6.742 8.25,4.875C8.25,3.007 6.742,1.5 4.875,1.5C3.007,1.5 1.5,3.007 1.5,4.875Z"
android:fillColor="#727778"
android:fillType="evenOdd"/>
</vector>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>

@ -59,9 +59,10 @@
android:textColor="@color/c_727778"
android:textSize="@dimen/sw_11sp" />
<include
android:id="@+id/ilPwd"
layout="@layout/item_input_pwd" />
<com.project.survey.widget.edittext.PasswordView
android:id="@+id/passwordView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tvError"

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tl="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/ilToolBar"
layout="@layout/toolbar" />
<include layout="@layout/item_search" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.flyco.tablayout.CommonTabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="54dp"
android:background="#ffffff"
tl:tl_iconHeight="23dp"
tl:tl_iconWidth="23dp"
tl:tl_indicator_color="#3498db"
tl:tl_indicator_height="0dp"
tl:tl_textSelectColor="#3498db"
tl:tl_textUnselectColor="#666666"
tl:tl_textsize="12sp"
tl:tl_underline_color="#cfcfcf"
tl:tl_underline_gravity="TOP"
tl:tl_underline_height="1dp" />
</LinearLayout>

@ -37,9 +37,10 @@
android:textColor="@color/c_727778"
android:textSize="@dimen/sw_11sp" />
<include
android:id="@+id/ilPwdOne"
layout="@layout/item_input_pwd" />
<com.project.survey.widget.edittext.PasswordView
android:id="@+id/passwordViewOne"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
@ -49,9 +50,20 @@
android:textColor="@color/c_727778"
android:textSize="@dimen/sw_11sp" />
<include
android:id="@+id/ilPwdTwo"
layout="@layout/item_input_pwd" />
<com.project.survey.widget.edittext.PasswordView
android:id="@+id/passwordViewTwo"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btnConfirm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/sw_60dp"
android:background="@drawable/bg_btn_login"
android:text="@string/confirm"
android:textColor="@color/white"
android:textSize="@dimen/sw_16sp" />
</LinearLayout>

@ -1,7 +1,6 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@ -31,12 +30,12 @@
android:id="@+id/tvCompany"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/sw_10dp"
android:paddingVertical="@dimen/sw_10dp"
android:text="某工程"
android:textColor="@color/white"
android:textSize="@dimen/sw_15sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="某工程" />
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="@dimen/sw_16dp"
android:paddingVertical="@dimen/sw_6dp"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingVertical="@dimen/sw_18dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_project_list" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/sw_14dp"
android:orientation="vertical">
<TextView
android:id="@+id/tvProjectName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/text_color_1"
android:textSize="@dimen/sw_15sp"
android:textStyle="bold"
android:text="xx工程" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/sw_3dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tvProjectNum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/text_color_2"
android:textSize="@dimen/sw_11sp"
android:text="XMBH2021-00001" />
<TextView
android:id="@+id/tvProjectArea"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/text_color_2"
android:textSize="@dimen/sw_11sp"
android:text="上海市长宁区" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/sw_30dp"
android:layout_marginHorizontal="@dimen/sw_16dp"
android:layout_marginVertical="@dimen/sw_7dp"
android:background="@drawable/bg_search"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="@dimen/sw_18dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_search" />
<EditText
android:id="@+id/etSearch"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
android:hint="请输入"
android:paddingStart="6dp"
android:textColor="@color/text_color_1"
android:textColorHint="@color/text_color_2"
android:textSize="@dimen/sw_11sp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/sw_2dp"
android:background="@drawable/bg_search_shadow_2" />
</LinearLayout>

@ -1,13 +1,12 @@
<?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">
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<EditText
android:id="@+id/etPwd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:paddingEnd="@dimen/sw_24dp"
android:textColor="@color/text_color_1"
android:textSize="@dimen/sw_14sp" />
@ -27,5 +26,4 @@
</FrameLayout>
</RelativeLayout>
</merge>

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2017 Yan Zhenjie
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.project.survey.widget.swiperecyclerview.SwipeMenuLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:contentViewId="@+id/swipe_content"
app:leftViewId="@+id/swipe_left"
app:rightViewId="@+id/swipe_right">
<com.project.survey.widget.swiperecyclerview.SwipeMenuView
android:id="@+id/swipe_left"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
<FrameLayout
android:id="@+id/swipe_content"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.project.survey.widget.swiperecyclerview.SwipeMenuView
android:id="@+id/swipe_right"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</com.project.survey.widget.swiperecyclerview.SwipeMenuLayout>

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2017 Yan Zhenjie
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.Holo.ProgressBar"
android:layout_width="32dp"
android:layout_height="32dp"
android:visibility="gone"
tools:visibility="invisible" />
<TextView
android:id="@+id/tv_load_more_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textColor="@color/text_color_2"
android:visibility="gone"
tools:text="Loading..."
tools:visibility="invisible" />
</merge>

@ -1,78 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--以下全部为ChatUI的属性-->
<declare-styleable name="BubbleView">
<attr name="arrowWidth" format="dimension" />
<attr name="angle" format="dimension" />
<attr name="arrowHeight" format="dimension" />
<attr name="arrowPosition" format="dimension" />
<attr name="bubbleColor" format="color" />
<attr name="arrowLocation" format="enum">
<enum name="left" value="0x00" />
<enum name="right" value="0x01" />
<enum name="top" value="0x02" />
<enum name="bottom" value="0x03" />
</attr>
</declare-styleable>
<declare-styleable name="RotateLayout">
<!-- Child view of this layout will be rotated by this angle. -->
<attr name="rotate_angle" format="integer" />
</declare-styleable>
<declare-styleable name="TextItem">
<attr name="text" format="string" />
<attr name="text_size" format="dimension" />
<attr name="text_color" format="color" />
<attr name="dividerVisible" format="enum">
<enum name="visible" value="0" />
<enum name="invisible" value="1" />
</attr>
<attr name="text_gravity" format="enum">
<enum name="start" value="0" />
<enum name="center" value="1" />
<enum name="end" value="2" />
</attr>
</declare-styleable>
<declare-styleable name="TextItemWithDescribe">
<attr name="describeValue" format="string" />
</declare-styleable>
<declare-styleable name="customAttrs">
<attr name="efp__selected_item_background" format="reference" />
<attr name="efp__ic_action_cancel" format="reference" />
<attr name="efp__ic_action_deselect" format="reference" />
<attr name="efp__ic_action_grid" format="reference" />
<attr name="efp__ic_action_invert_selection" format="reference" />
<attr name="efp__ic_action_list" format="reference" />
<attr name="efp__ic_action_new_folder" format="reference" />
<attr name="efp__ic_action_ok" format="reference" />
<attr name="efp__ic_action_select_all" format="reference" />
<attr name="efp__ic_action_sort" format="reference" />
<attr name="efp__ic_action_storage" format="reference" />
</declare-styleable>
<declare-styleable name="SwitchWithText">
<attr name="name" format="string" />
<attr name="onText" format="string" />
<attr name="offText" format="string" />
<attr name="onChecked" format="boolean" />
</declare-styleable>
<declare-styleable name="EditSpinner">
<attr name="hint" format="string"/>
<attr name="rightImage" format="reference"/>
<attr name="Background" format="reference"/>
<attr name="maxLine" format="integer"/>
<declare-styleable name="Verificationcode">
<attr name="code_text_color" format="color" />//验证码字体颜色
<attr name="code_text_size" format="dimension" />//验证码字体大小
<attr name="code_number" format="integer" />//验证码数量 4位 6位
<attr name="line_color_default" format="color" />//验证码下面线的默认颜色
<attr name="line_color_focus" format="color" />//验证码下面线选中后的颜色
</declare-styleable>
<declare-styleable name="Verificationcode">
<attr format="color" name="code_text_color"/>//验证码字体颜色
<attr format="dimension" name="code_text_size"/>//验证码字体大小
<attr format="integer" name="code_number"/>//验证码数量 4位 6位
<attr format="color" name="line_color_default"/>//验证码下面线的默认颜色
<attr format="color" name="line_color_focus"/>//验证码下面线选中后的颜色
<declare-styleable name="SwipeMenuLayout">
<attr name="leftViewId" format="reference|integer" />
<attr name="rightViewId" format="reference|integer" />
<attr name="contentViewId" format="reference|integer" />
</declare-styleable>
</resources>

@ -5,13 +5,14 @@
<!--新增-->
<color name="bg_content">#f8f8f8</color>
<color name="text_color_1">#333333</color>
<color name="text_color_2">#999</color>
<color name="text_color_2">#727778</color>
<color name="white">#ffffff</color>
<color name="black">#000000</color>
<color name="c_2964da">#2964DA</color>
<color name="c_ebebeb">#EBEBEB</color>
<color name="c_727778">#727778</color>
<color name="c_dfdfdf">#DFDFDF</color>
<color name="hor_line">#EBEBEB</color>
</resources>

@ -47,5 +47,10 @@
<string name="set_new_password">设置新密码</string>
<string name="please_enter_new_password">请输入新的密码。</string>
<string name="confirm_password">确认密码</string>
<string name="project_list">项目列表</string>
<string name="x_recycler_data_empty">There is no data</string>
<string name="x_recycler_more_not">No more data</string>
<string name="x_recycler_click_load_more">Click to load more</string>
<string name="x_recycler_load_error">Error, please try again</string>
<string name="x_recycler_load_more_message">Loading, please wait later</string>
</resources>
Loading…
Cancel
Save