parent
d51ff9e71d
commit
077a84239f
63 changed files with 5788 additions and 429 deletions
@ -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") |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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> |
@ -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> |
@ -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> |
||||
|
@ -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> |
Loading…
Reference in new issue