diff --git a/app/src/main/java/com/zj365/dc/activity/shop/CreateGoodsOrderAct.kt b/app/src/main/java/com/zj365/dc/activity/shop/CreateGoodsOrderAct.kt index 890f21a..eaf2595 100644 --- a/app/src/main/java/com/zj365/dc/activity/shop/CreateGoodsOrderAct.kt +++ b/app/src/main/java/com/zj365/dc/activity/shop/CreateGoodsOrderAct.kt @@ -66,8 +66,8 @@ class CreateGoodsOrderAct : BaseVmAct() { } mViewModel.computeAmountLiveData.observe(this){ - binding.tvTotalPrice.text ="¥ ${it.data}" - + binding.tvTotalPrice.text ="¥ ${it.data.pay_price}" + binding.tvPlatformDiscounts.text ="¥ ${it.data.discount_price}" couponBean?.let { binding.tvDiscounts.text = "可优惠¥ ${it.as_amount}" // binding.tvPlatformDiscounts.text = "${(bean!!.ori_price * bean!!.skuNum) - (it.skuPrice * it.skuNum) - couponBean.as_amount}" @@ -102,7 +102,7 @@ class CreateGoodsOrderAct : BaseVmAct() { binding.tvSpec.text = bean.skuName binding.tvNum.text = "数量:${bean.skuNum}" binding.tvPrice.text = "商品原价:¥ ${bean.ori_price}" - binding.tvPlatformDiscounts.text = "¥ ${(bean.ori_price * bean.skuNum) - (bean.skuPrice * bean.skuNum)}" + // binding.tvPlatformDiscounts.text = "¥ ${(bean.ori_price * bean.skuNum) - (bean.skuPrice * bean.skuNum)}" if (bean.delivery_mode == 0){ //0,无需发货,1,需要发货 binding.rxAddress.visibility = View.GONE diff --git a/app/src/main/java/com/zj365/dc/fragment/DynamicManagementFrag.kt b/app/src/main/java/com/zj365/dc/fragment/DynamicManagementFrag.kt index 81b9fc8..09895ff 100644 --- a/app/src/main/java/com/zj365/dc/fragment/DynamicManagementFrag.kt +++ b/app/src/main/java/com/zj365/dc/fragment/DynamicManagementFrag.kt @@ -734,9 +734,10 @@ class DynamicManagementFrag : BaseVmFrag() , EasyPermission functionList.addAll(it.data) if (showIdInfo ==MMkvHelper.getLong(Const.USER_ID).toString()) { Const.functionListDate.value = functionList + startUpdateHealthInfoLive() } - startUpdateHealthInfoLive() + } //获取首页所有信息返回 @@ -810,7 +811,7 @@ class DynamicManagementFrag : BaseVmFrag() , EasyPermission } // } - if(!isJob){ + if(!isJob || it.data.user?.id.toString() != MMkvHelper.getLong(Const.USER_ID).toString()){ get24HourReportInfo(showIdInfo)// 这里调用一次接口,防止首次进入App不会刷新评估状态 } //设置健康监测按钮列表数据 @@ -891,12 +892,12 @@ class DynamicManagementFrag : BaseVmFrag() , EasyPermission binding.mHeader.mBtnBind.visibility = View.GONE binding.mHeader.mLayoutResult.visibility = View.VISIBLE } else { //没有绑定手表 - if (/*mFamilyAdapter.position == 0 &&*/ it.data.user?.id.toString() == MMkvHelper.getLong(Const.USER_ID).toString()) { //是自己,显示绑定手表 + if (it.data.user?.id.toString() == MMkvHelper.getLong(Const.USER_ID).toString()) { //是自己,显示绑定手表 binding.mHeader.mBtnBind.visibility = View.VISIBLE binding.mHeader.mLayoutResult.visibility = View.GONE } else { //不是自己隐藏数据 binding.mHeader.mBtnBind.visibility = View.GONE - binding.mHeader.mLayoutResult.visibility = View.GONE + binding.mHeader.mLayoutResult.visibility = View.VISIBLE } } updateHealthDesc(healthResult) diff --git a/app/src/main/java/com/zj365/dc/vm/ShopMallVm.kt b/app/src/main/java/com/zj365/dc/vm/ShopMallVm.kt index 7f7c671..03172df 100644 --- a/app/src/main/java/com/zj365/dc/vm/ShopMallVm.kt +++ b/app/src/main/java/com/zj365/dc/vm/ShopMallVm.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.MutableLiveData import com.alibaba.fastjson.JSONObject import com.xty.base.vm.BaseVm import com.xty.network.model.ChooseAddressBean +import com.xty.network.model.ComputeAmountBean import com.xty.network.model.GoodsRecordsBean import com.xty.network.model.RespBody import com.xty.network.model.ShopCategoryBean @@ -21,7 +22,7 @@ class ShopMallVm : BaseVm() { val hasCouponLiveData by lazy { MutableLiveData> ()} - val computeAmountLiveData by lazy { MutableLiveData>() } + val computeAmountLiveData by lazy { MutableLiveData>() } fun getGoodsCategory(parentId:Long){ startHttp { diff --git a/common/src/main/java/com/xty/common/weight/PinnedHeaderItemDecoration.java b/common/src/main/java/com/xty/common/weight/PinnedHeaderItemDecoration.java deleted file mode 100644 index 1c540e9..0000000 --- a/common/src/main/java/com/xty/common/weight/PinnedHeaderItemDecoration.java +++ /dev/null @@ -1,246 +0,0 @@ -package com.xty.common.weight; - -import android.graphics.Canvas; -import android.graphics.Rect; -import android.graphics.Region; -import android.view.View; -import android.view.ViewGroup; - -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import java.util.HashMap; -import java.util.Map; - -public class PinnedHeaderItemDecoration extends RecyclerView.ItemDecoration { - - private final static String TAG = PinnedHeaderItemDecoration.class.getSimpleName(); - - /** - * 当前绘制的pinnedheaderview - */ - View mPinnedHeaderView = null; - - /** - * pinnedheaderview的位置 - */ - int mHeaderPosition = -1; - - /** - * 装载所有的viewtype - */ - Map mPinnedViewTypes = new HashMap(); - - /** - * pinnedheaderview的上边距 - */ - private int mPinnedHeaderTop; - - /** - * pinnedheaderview的裁剪区域 - */ - private Rect mClipBounds; - private Builder mBuilder; - private int mFirstVisiblePosition; - - private PinnedHeaderItemDecoration(Builder builder) { - - mBuilder = builder; - } - - /** - * @param c - * @param parent - * @param state - */ - @Override - public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { - createPinnedHeader(parent); - - if (mPinnedHeaderView != null) { - // check overlap section view. - //TODO support only vertical header currently. - final int headerEndAt = mPinnedHeaderView.getTop() + mPinnedHeaderView.getHeight() + 1; - final View v = parent.findChildViewUnder(c.getWidth() / 2, headerEndAt); - if (isPinnedView(parent, v)) { - mPinnedHeaderTop = v.getTop() - mPinnedHeaderView.getHeight(); - } else { - mPinnedHeaderTop = 0; - } - - if (isHeaderView(mFirstVisiblePosition)) { - return; - } - mClipBounds = c.getClipBounds(); - mClipBounds.top = mPinnedHeaderTop + mPinnedHeaderView.getHeight(); - c.clipRect(mClipBounds); - } - } - - - /** - * @param c - * @param parent - * @param state - */ - @Override - public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { - if (mPinnedHeaderView != null && !isHeaderView(mFirstVisiblePosition)) { - c.save(); - - mClipBounds.top = 0; - c.clipRect(mClipBounds, Region.Op.UNION); - c.translate(0, mPinnedHeaderTop); - mPinnedHeaderView.draw(c); - - c.restore(); - } - } - - private void createPinnedHeader(RecyclerView parent) { - checkCache(parent); - - // get LinearLayoutManager. - //final LinearLayoutManager linearLayoutManager = getLayoutManager(parent); - final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); - if (layoutManager instanceof LinearLayoutManager) { - mFirstVisiblePosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); - } else if (layoutManager instanceof GridLayoutManager) { - mFirstVisiblePosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition(); - } else { - return; - } - - // mFirstVisiblePosition = gridLayoutManager.findFirstVisibleItemPosition(); - - final int headerPosition = findPinnedHeaderPosition(mFirstVisiblePosition); - - if (isHeaderView(mFirstVisiblePosition)) { - return; - } - - if (headerPosition >= 0 && mHeaderPosition != headerPosition) { - mHeaderPosition = headerPosition; - final int viewType = mBuilder.mStickProvider.getItemViewType(headerPosition); - - final RecyclerView.ViewHolder pinnedViewHolder = mBuilder.mStickProvider.createViewHolder(parent, viewType); - mBuilder.mStickProvider.bindViewHolder(pinnedViewHolder, headerPosition); - mPinnedHeaderView = pinnedViewHolder.itemView; - - // read layout parameters - ViewGroup.LayoutParams layoutParams = mPinnedHeaderView.getLayoutParams(); - if (layoutParams == null) { - layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - mPinnedHeaderView - .setLayoutParams(layoutParams); - } - - - int heightMode = View.MeasureSpec.getMode(layoutParams.height); - int heightSize = View.MeasureSpec.getSize(layoutParams.height); - - if (heightMode == View.MeasureSpec.UNSPECIFIED) { - heightMode = View.MeasureSpec.EXACTLY; - } - - final int maxHeight = parent.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom(); - if (heightSize > maxHeight) { - heightSize = maxHeight; - } - - // measure & layout - final int ws = View.MeasureSpec.makeMeasureSpec(parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.EXACTLY); - final int hs = View.MeasureSpec.makeMeasureSpec(heightSize, heightMode); - mPinnedHeaderView.measure(ws, hs); - - mPinnedHeaderView.layout(0, 0, mPinnedHeaderView.getMeasuredWidth(), mPinnedHeaderView.getMeasuredHeight()); - } - } - - - /** - * return the first visible view position is headerview - * - * @param firstVisiblePosition first visible view position - */ - private boolean isHeaderView(int firstVisiblePosition) { - final int position = firstVisiblePosition - mBuilder.mStickProvider.getHeaderCount(); - if (position < 0) { - return true; - } - return false; - } - - - private int findPinnedHeaderPosition(int fromPosition) { - if (fromPosition > mBuilder.mStickProvider.getItemCount()) { - return -1; - } - - for (int position = fromPosition; position >= 0; position--) { - final int viewType = mBuilder.mStickProvider.getItemViewType(position); - if (isPinnedViewType(viewType)) { - return position; - } - } - - return -1; - } - - private boolean isPinnedViewType(int viewType) { - if (!mPinnedViewTypes.containsKey(viewType)) { - mPinnedViewTypes.put(viewType, mBuilder.mStickProvider.isPinnedViewType(viewType)); - } - return mPinnedViewTypes.get(viewType); - } - - private boolean isPinnedView(RecyclerView parent, View v) { - final int position = parent.getChildAdapterPosition(v) /*- mBuilder.mStickProvider.getHeaderCount()*/; - if (position == RecyclerView.NO_POSITION) { - return false; - } - final int viewType = mBuilder.mStickProvider.getItemViewType(position); - - return isPinnedViewType(viewType); - } - - private void checkCache(RecyclerView parent) { - RecyclerView.Adapter adapter = parent.getAdapter(); - if (mBuilder.mStickProvider != adapter) { - disableCache(); - } - } - - - private void disableCache() { - mPinnedHeaderView = null; - mHeaderPosition = -1; - mPinnedViewTypes.clear(); - } - - public static Builder builder() { - return new Builder(); - } - - - public static class Builder { - - private AdapterStick mStickProvider; - private PinnedHeaderItemDecoration mPinnedHeaderItemDecoration; - - - public Builder adapterProvider(AdapterStick stickProvider) { - mStickProvider = stickProvider; - return this; - } - - public PinnedHeaderItemDecoration build() { - if (mPinnedHeaderItemDecoration == null) { - mPinnedHeaderItemDecoration = new PinnedHeaderItemDecoration(this); - } - return mPinnedHeaderItemDecoration; - } - } - -} diff --git a/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/PinnedHeaderItemDecoration.java b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/PinnedHeaderItemDecoration.java new file mode 100644 index 0000000..80d97f3 --- /dev/null +++ b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/PinnedHeaderItemDecoration.java @@ -0,0 +1,684 @@ +package com.xty.common.weight.pinnedsectionitemdecoration; + +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.Region; +import android.graphics.drawable.Drawable; + +import android.view.View; +import android.view.ViewGroup; + + +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.StaggeredGridLayoutManager; + +import com.xty.common.R; +import com.xty.common.weight.pinnedsectionitemdecoration.callback.OnHeaderClickListener; +import com.xty.common.weight.pinnedsectionitemdecoration.callback.OnItemTouchListener; +import com.xty.common.weight.pinnedsectionitemdecoration.utils.DividerHelper; + +import java.lang.reflect.Field; +import java.util.ArrayList; + +/** + * Created by Oubowu on 2016/7/21 15:38. + *

这个是单独一个布局的标签

+ *

porting from https://github.com/takahr/pinned-section-item-decoration

+ *

注意:标签所在最外层布局不能设置marginTop,因为往上滚动遮不住真正的标签;marginBottom还有问题待解决

+ */ +public class PinnedHeaderItemDecoration extends RecyclerView.ItemDecoration { + + private OnHeaderClickListener mHeaderClickListener; + + private boolean mEnableDivider; + + private boolean mDisableHeaderClick; + + private int mDividerId; + + private int[] mClickIds; + + private Drawable mDrawable; + private RecyclerView.Adapter mAdapter; + + // 缓存的标签 + private View mPinnedHeaderView; + + // 缓存的标签位置 + private int mPinnedHeaderPosition = -1; + + // 顶部标签的Y轴偏移值 + private int mPinnedHeaderOffset; + + // 用于锁定画布绘制范围 + private Rect mClipBounds; + + // 父布局的左间距 + private int mRecyclerViewPaddingLeft; + // 父布局的顶间距 + private int mRecyclerViewPaddingTop; + + private int mHeaderLeftMargin; + private int mHeaderTopMargin; + private int mHeaderRightMargin; + private int mHeaderBottomMargin; + + // 用于处理头部点击事件屏蔽与响应 + private OnItemTouchListener mItemTouchListener; + + private int mLeft; + private int mTop; + private int mRight; + private int mBottom; + + private int mFirstVisiblePosition; + + private int mDataPositionOffset; + + private int mPinnedHeaderType; + + private boolean mDisableDrawHeader; + + private RecyclerView mParent; + + // 当我们调用mRecyclerView.addItemDecoration()方法添加decoration的时候,RecyclerView在绘制的时候,去会绘制decorator,即调用该类的onDraw和onDrawOver方法, + // 1.onDraw方法先于drawChildren + // 2.onDrawOver在drawChildren之后,一般我们选择复写其中一个即可。 + // 3.getItemOffsets 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator。 + + private PinnedHeaderItemDecoration(Builder builder) { + mEnableDivider = builder.enableDivider; + mHeaderClickListener = builder.headerClickListener; + mDividerId = builder.dividerId; + mClickIds = builder.clickIds; + mDisableHeaderClick = builder.disableHeaderClick; + mPinnedHeaderType = builder.pinnedHeaderType; + } + + @Override + public void getItemOffsets(final Rect outRect, final View view, final RecyclerView parent, RecyclerView.State state) { + + checkCache(parent); + + if (!mEnableDivider) { + return; + } + + if (mDrawable == null) { + mDrawable = ContextCompat.getDrawable(parent.getContext(), mDividerId != 0 ? mDividerId : R.drawable.divider); + } + + if (parent.getLayoutManager() instanceof GridLayoutManager) { + if (!isPinnedHeader(parent, view)) { + final int spanCount = getSpanCount(parent); + int position = parent.getChildAdapterPosition(view); + if (isFirstColumn(parent, position, spanCount)) { + // 第一列要多画左边 + outRect.set(mDrawable.getIntrinsicWidth(), 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()); + } else { + outRect.set(0, 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()); + } + } else { + // 标签画底部分隔线 + outRect.set(0, 0, 0, mDrawable.getIntrinsicHeight()); + } + } else if (parent.getLayoutManager() instanceof LinearLayoutManager) { + outRect.set(0, 0, 0, mDrawable.getIntrinsicHeight()); + } else if (parent.getLayoutManager() instanceof StaggeredGridLayoutManager) { + if (isPinnedHeader(parent, view)) { + outRect.set(0, 0, 0, mDrawable.getIntrinsicHeight()); + } else { + final StaggeredGridLayoutManager.LayoutParams slp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams(); + // slp.getSpanIndex(): 这个可以拿到它在同一行排序的真实顺序 + if (slp.getSpanIndex() == 0) { + outRect.set(mDrawable.getIntrinsicWidth(), 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()); + } else { + outRect.set(0, 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()); + } + } + } + } + + + @Override + public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + + // 检测到标签存在的时候,将标签强制固定在顶部 + createPinnedHeader(parent); + + if (!mDisableDrawHeader && mPinnedHeaderView != null && mFirstVisiblePosition >= mPinnedHeaderPosition) { + + mClipBounds = c.getClipBounds(); + // getTop拿到的是它的原点(它自身的padding值包含在内)相对parent的顶部距离,加上它的高度后就是它的底部所处的位置 + final int headEnd = mPinnedHeaderView.getTop() + mPinnedHeaderView.getHeight(); + // 根据坐标查找view,headEnd + 1找到的就是mPinnedHeaderView底部下面的view +// final View belowView = parent.findChildViewUnder(c.getWidth() / 2, headEnd + 1); + // 解决头部闪烁问题 + final View belowView = parent.findChildViewUnder(c.getWidth() / 2, headEnd); + int belowViewAdapterPosition = parent.getChildAdapterPosition(belowView); + if (belowViewAdapterPosition > mFirstVisiblePosition && isPinnedHeader(parent, belowView)) { + // 如果是标签的话,缓存的标签就要同步跟此标签移动 + // 根据belowView相对顶部距离计算出缓存标签的位移 + mPinnedHeaderOffset = belowView.getTop() - (mRecyclerViewPaddingTop + mPinnedHeaderView.getHeight() + mHeaderTopMargin); + // 锁定的矩形顶部为v.getTop(趋势是mPinnedHeaderView.getHeight()->0) + // mClipBounds.top = belowView.getTop(); + mClipBounds.top = mRecyclerViewPaddingTop; + } else { + mPinnedHeaderOffset = 0; + // mClipBounds.top = mRecyclerViewPaddingTop + mPinnedHeaderView.getHeight()*0; + mClipBounds.top = mRecyclerViewPaddingTop; + } + // 锁定画布绘制范围,记为A + c.clipRect(mClipBounds); + } + + if (mEnableDivider) { + drawDivider(c, parent); + } + + } + + // 画分隔线 + private void drawDivider(Canvas c, RecyclerView parent) { + + if (mAdapter == null) { + // checkCache的话RecyclerView未设置之前mAdapter为空 + return; + } + + // 不让分隔线画出界限 + c.clipRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getWidth() - parent.getPaddingRight(), parent.getHeight() - parent.getPaddingBottom()); + + if (parent.getLayoutManager() instanceof GridLayoutManager) { + int childCount = parent.getChildCount(); + final int spanCount = getSpanCount(parent); + for (int i = 0; i < childCount; i++) { + final View child = parent.getChildAt(i); + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + // 要考虑View的重用啊 + int realPosition = parent.getChildAdapterPosition(child); + if (isPinnedHeaderType(mAdapter.getItemViewType(realPosition))) { + DividerHelper.drawBottomAlignItem(c, mDrawable, child, params); + } else { + if (isFirstColumn(parent, realPosition, spanCount)) { + DividerHelper.drawLeft(c, mDrawable, child, params); + } + DividerHelper.drawBottom(c, mDrawable, child, params); + DividerHelper.drawRight(c, mDrawable, child, params); + } + } + } else if (parent.getLayoutManager() instanceof LinearLayoutManager) { + int childCount = parent.getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = parent.getChildAt(i); + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + DividerHelper.drawBottomAlignItem(c, mDrawable, child, params); + } + } else if (parent.getLayoutManager() instanceof StaggeredGridLayoutManager) { + int childCount = parent.getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = parent.getChildAt(i); + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + if (isPinnedHeader(parent, child)) { + DividerHelper.drawBottomAlignItem(c, mDrawable, child, params); + } else { + DividerHelper.drawLeft(c, mDrawable, child, params); + DividerHelper.drawBottom(c, mDrawable, child, params); + DividerHelper.drawRight(c, mDrawable, child, params); + } + } + } + + } + + @Override + public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { + + if (!mDisableDrawHeader && mPinnedHeaderView != null && mFirstVisiblePosition >= mPinnedHeaderPosition) { + c.save(); + + if (mItemTouchListener != null) { + mItemTouchListener.invalidTopAndBottom(mPinnedHeaderOffset); + } + + mClipBounds.top = mRecyclerViewPaddingTop + mHeaderTopMargin; + // 锁定画布绘制范围,记为B + // REVERSE_DIFFERENCE,实际上就是求得的B和A的差集范围,即B-A,只有在此范围内的绘制内容才会被显示 + // 因此,只绘制(0,0,parent.getWidth(),belowView.getTop())这个范围,然后画布移动了mPinnedHeaderTop,所以刚好是绘制顶部标签移动的范围 + // 低版本不行,换回Region.Op.UNION并集 + + // 解决编译版本28修改后的抛出异常:java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed + c.clipRect(mClipBounds, Region.Op.INTERSECT); + + c.translate(mRecyclerViewPaddingLeft + mHeaderLeftMargin, mPinnedHeaderOffset + mRecyclerViewPaddingTop + mHeaderTopMargin); + mPinnedHeaderView.draw(c); + + c.restore(); + + } else if (mItemTouchListener != null) { + // 不绘制的时候,把头部的偏移值偏移用户点击不到的程度 + mItemTouchListener.invalidTopAndBottom(-1000); + } + } + + /** + * 查找到view对应的位置从而判断出是否标签类型 + * + * @param parent + * @param view + * @return + */ + private boolean isPinnedHeader(RecyclerView parent, View view) { + final int position = parent.getChildAdapterPosition(view); + if (position == RecyclerView.NO_POSITION) { + return false; + } + final int type = mAdapter.getItemViewType(position); + return isPinnedHeaderType(type); + } + + /** + * 创建标签强制固定在顶部 + * + * @param parent + */ + @SuppressWarnings("unchecked") + private void createPinnedHeader(final RecyclerView parent) { + + if (mAdapter == null) { + // checkCache的话RecyclerView未设置之前mAdapter为空 + return; + } + + final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); + + // 获取第一个可见的item位置 + mFirstVisiblePosition = findFirstVisiblePosition(layoutManager); + + // 获取标签的位置, + int pinnedHeaderPosition = findPinnedHeaderPosition(mFirstVisiblePosition); + if (pinnedHeaderPosition >= 0 && mPinnedHeaderPosition != pinnedHeaderPosition) { + + // 标签位置有效并且和缓存的位置不同 + mPinnedHeaderPosition = pinnedHeaderPosition; + // 获取标签的type + final int type = mAdapter.getItemViewType(mPinnedHeaderPosition); + + // 手动调用创建标签 + final RecyclerView.ViewHolder holder = mAdapter.createViewHolder(parent, type); + mAdapter.bindViewHolder(holder, mPinnedHeaderPosition); + // 缓存标签 + mPinnedHeaderView = holder.itemView; + + ViewGroup.LayoutParams lp = mPinnedHeaderView.getLayoutParams(); + if (lp == null) { + // 标签默认宽度占满parent + lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + mPinnedHeaderView.setLayoutParams(lp); + } + + // 对高度进行处理 + int heightMode; + int heightSize; + + if (lp.height >= 0) { + heightMode = View.MeasureSpec.EXACTLY; + heightSize = lp.height; + } else if (lp.height == ViewGroup.LayoutParams.MATCH_PARENT) { + heightMode = View.MeasureSpec.EXACTLY; + heightSize = parent.getHeight(); + } else if (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { + heightMode = View.MeasureSpec.AT_MOST; + heightSize = parent.getHeight(); + } else { + heightMode = View.MeasureSpec.AT_MOST; + heightSize = parent.getHeight(); + } + + mRecyclerViewPaddingLeft = parent.getPaddingLeft(); + int recyclerViewPaddingRight = parent.getPaddingRight(); + mRecyclerViewPaddingTop = parent.getPaddingTop(); + int recyclerViewPaddingBottom = parent.getPaddingBottom(); + + if (lp instanceof ViewGroup.MarginLayoutParams) { + final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp; + mHeaderLeftMargin = mlp.leftMargin; + mHeaderTopMargin = mlp.topMargin; + mHeaderRightMargin = mlp.rightMargin; + mHeaderBottomMargin = mlp.bottomMargin; + } + + // 最大高度为RecyclerView的高度减去padding + final int maxHeight = parent.getHeight() - mRecyclerViewPaddingTop - recyclerViewPaddingBottom; + // 不能超过maxHeight + heightSize = Math.min(heightSize, maxHeight); + + // 因为标签默认宽度占满parent,所以宽度强制为RecyclerView的宽度减去padding + final int widthSpec = View.MeasureSpec + .makeMeasureSpec(parent.getWidth() - mRecyclerViewPaddingLeft - recyclerViewPaddingRight - mHeaderLeftMargin - mHeaderRightMargin, + View.MeasureSpec.EXACTLY); + final int heightSpec = View.MeasureSpec.makeMeasureSpec(heightSize, heightMode); + // 强制测量 + mPinnedHeaderView.measure(widthSpec, heightSpec); + + mLeft = mRecyclerViewPaddingLeft + mHeaderLeftMargin; + mRight = mPinnedHeaderView.getMeasuredWidth() + mLeft; + mTop = mRecyclerViewPaddingTop + mHeaderTopMargin; + mBottom = mPinnedHeaderView.getMeasuredHeight() + mTop; + + // 位置强制布局在顶部 + mPinnedHeaderView.layout(mLeft, mTop, mRight, mBottom); + + if (mItemTouchListener == null && mHeaderClickListener != null) { + mItemTouchListener = new OnItemTouchListener(parent.getContext()); + try { + final Field field = parent.getClass().getDeclaredField("mOnItemTouchListeners"); + field.setAccessible(true); + final ArrayList touchListeners = (ArrayList) field.get(parent); + touchListeners.add(0, mItemTouchListener); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + parent.addOnItemTouchListener(mItemTouchListener); + } catch (IllegalAccessException e) { + e.printStackTrace(); + parent.addOnItemTouchListener(mItemTouchListener); + } catch (Exception e) { + // 防止android 9禁止了反射调用 + e.printStackTrace(); + parent.addOnItemTouchListener(mItemTouchListener); + } + mItemTouchListener.setHeaderClickListener(mHeaderClickListener); + mItemTouchListener.disableHeaderClick(mDisableHeaderClick); + mItemTouchListener.setClickBounds(OnItemTouchListener.HEADER_ID, mPinnedHeaderView); + } + + if (mHeaderClickListener != null) { + // OnItemTouchListener.HEADER_ID代表是标签的Id + mItemTouchListener.setClickBounds(OnItemTouchListener.HEADER_ID, mPinnedHeaderView); + if (mHeaderClickListener != null && mClickIds != null && mClickIds.length > 0) { + for (int mClickId : mClickIds) { + final View view = mPinnedHeaderView.findViewById(mClickId); + if (view != null && view.getVisibility() == View.VISIBLE) { + mItemTouchListener.setClickBounds(mClickId, view); + } + } + } + mItemTouchListener.setClickHeaderInfo(mPinnedHeaderPosition - mDataPositionOffset); + } + + } + + } + + public int getDataPositionOffset() { + return mDataPositionOffset; + } + + public void setDataPositionOffset(int offset) { + mDataPositionOffset = offset; + } + + /** + * 从传入位置递减找出标签的位置 + * + * @param formPosition + * @return + */ + private int findPinnedHeaderPosition(int formPosition) { + + for (int position = formPosition; position >= 0; position--) { + // 位置递减,只要查到位置是标签,立即返回此位置 + final int type = mAdapter.getItemViewType(position); + if (isPinnedHeaderType(type)) { + return position; + } + } + + return -1; + } + + /** + * 通过适配器告知类型是否为标签 + * + * @param type + * @return + */ + private boolean isPinnedHeaderType(int type) { + return mPinnedHeaderType == type; + } + + /** + * 找出第一个可见的Item的位置 + * + * @param layoutManager + * @return + */ + private int findFirstVisiblePosition(RecyclerView.LayoutManager layoutManager) { + int firstVisiblePosition = 0; + if (layoutManager instanceof GridLayoutManager) { + firstVisiblePosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition(); + } else if (layoutManager instanceof LinearLayoutManager) { + firstVisiblePosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); + } else if (layoutManager instanceof StaggeredGridLayoutManager) { + int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()]; + ((StaggeredGridLayoutManager) layoutManager).findFirstVisibleItemPositions(into); + firstVisiblePosition = Integer.MAX_VALUE; + for (int pos : into) { + firstVisiblePosition = Math.min(pos, firstVisiblePosition); + } + } + return firstVisiblePosition; + } + + /** + * 检查缓存 + * + * @param parent + */ + private void checkCache(final RecyclerView parent) { + + if (mParent != parent) { + mParent = parent; + } + + final RecyclerView.Adapter adapter = parent.getAdapter(); + if (mAdapter != adapter) { + // 适配器为null或者不同,清空缓存 + mPinnedHeaderView = null; + mPinnedHeaderPosition = -1; + mAdapter = adapter; + mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + super.onChanged(); + reset(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + super.onItemRangeChanged(positionStart, itemCount); + reset(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { + super.onItemRangeChanged(positionStart, itemCount, payload); + reset(); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + super.onItemRangeInserted(positionStart, itemCount); + reset(); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + super.onItemRangeRemoved(positionStart, itemCount); + reset(); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + super.onItemRangeMoved(fromPosition, toPosition, itemCount); + reset(); + } + }); + } + } + + private void reset() { + mPinnedHeaderPosition = -1; + mPinnedHeaderView = null; + } + + /** + * 适用于网格布局,用于判断是否是第一列 + * + * @param parent + * @param pos + * @param spanCount + * @return + */ + private boolean isFirstColumn(RecyclerView parent, int pos, int spanCount) { + RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); + if (layoutManager instanceof GridLayoutManager) { + final int headerPosition = findPinnedHeaderPosition(pos); + if (headerPosition >= 0 && (pos - (headerPosition + 1)) % spanCount == 0) { + // 找到头部位置减去包括头部位置之前的个数 + return true; + } + } + return false; + } + + private int getSpanCount(RecyclerView parent) { + // 列数 + int spanCount = -1; + RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); + if (layoutManager instanceof GridLayoutManager) { + spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); + } else if (layoutManager instanceof StaggeredGridLayoutManager) { + spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount(); + } + return spanCount; + } + + public View getPinnedHeaderView() { + return mPinnedHeaderView; + } + + public int getPinnedHeaderPosition() { + return mPinnedHeaderPosition; + } + + /** + * 是否禁止绘制粘性头部 + * + * @return true的话不绘制头部 + */ + public boolean isDisableDrawHeader() { + return mDisableDrawHeader; + } + + /** + * 禁止绘制粘性头部 + * + * @param disableDrawHeader true的话不绘制头部,默认false绘制头部 + */ + public void disableDrawHeader(boolean disableDrawHeader) { + mDisableDrawHeader = disableDrawHeader; + if (mParent != null) { + mParent.invalidateItemDecorations(); + } + } + + public static class Builder { + + private OnHeaderClickListener headerClickListener; + + private int dividerId; + + private boolean enableDivider; + + private int[] clickIds; + + private boolean disableHeaderClick; + + private int pinnedHeaderType; + + /** + * 构造方法 + * + * @param pinnedHeaderType 粘性标签的类型 + */ + public Builder(int pinnedHeaderType) { + this.pinnedHeaderType = pinnedHeaderType; + } + + /** + * 设置标签和其内部的子控件的监听,若设置点击监听不为null,但是disableHeaderClick(true)的话,还是不会响应点击事件 + * + * @param headerClickListener 监听,若不设置这个setClickIds无效 + * @return 构建者 + */ + public Builder setHeaderClickListener(OnHeaderClickListener headerClickListener) { + this.headerClickListener = headerClickListener; + return this; + } + + /** + * 设置分隔线资源ID + * + * @param dividerId 资源ID,若不设置这个并且enableDivider=true时,使用默认的分隔线 + * @return 构建者 + */ + public Builder setDividerId(int dividerId) { + this.dividerId = dividerId; + return this; + } + + /** + * 是否开启绘制分隔线,默认关闭 + * + * @param enableDivider true为绘制,false不绘制,false时setDividerId无效 + * @return 构建者 + */ + public Builder enableDivider(boolean enableDivider) { + this.enableDivider = enableDivider; + return this; + } + + /** + * 通过传入包括标签和其内部的子控件的ID设置其对应的点击事件 + * + * @param clickIds 标签或其内部的子控件的ID + * @return 构建者 + */ + public Builder setClickIds(int... clickIds) { + this.clickIds = clickIds; + return this; + } + + /** + * 是否关闭标签点击事件,默认开启 + * + * @param disableHeaderClick true为关闭标签点击事件,false为开启标签点击事件 + * @return 构建者 + */ + public Builder disableHeaderClick(boolean disableHeaderClick) { + this.disableHeaderClick = disableHeaderClick; + return this; + } + + public PinnedHeaderItemDecoration create() { + return new PinnedHeaderItemDecoration(this); + } + + } + + +} diff --git a/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/SmallPinnedHeaderItemDecoration.java b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/SmallPinnedHeaderItemDecoration.java new file mode 100644 index 0000000..1a48ec1 --- /dev/null +++ b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/SmallPinnedHeaderItemDecoration.java @@ -0,0 +1,617 @@ +package com.xty.common.weight.pinnedsectionitemdecoration; + +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.Region; +import android.graphics.drawable.Drawable; + +import android.view.View; +import android.view.ViewGroup; + + +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.StaggeredGridLayoutManager; + +import com.xty.common.R; +import com.xty.common.weight.pinnedsectionitemdecoration.callback.OnHeaderClickListener; +import com.xty.common.weight.pinnedsectionitemdecoration.callback.OnItemTouchListener; +import com.xty.common.weight.pinnedsectionitemdecoration.utils.DividerHelper; + +import java.lang.reflect.Field; +import java.util.ArrayList; + +/** + * Created by Oubowu on 2016/7/21 15:38. + *

这个是附在数据布局的小标签,只支持LinearLayoutManager或者GridLayoutManager且一行只有一列的情况,这个比较符合使用场景

+ *

注意:标签不能设置marginTop,因为往上滚动遮不住真正的标签

+ */ +public class SmallPinnedHeaderItemDecoration extends RecyclerView.ItemDecoration { + + // 取出Adapter + private RecyclerView.Adapter mAdapter = null; + // 标签的id值 + private int mPinnedHeaderId; + private OnHeaderClickListener mHeaderClickListener; + private int[] mClickIds; + private int mDividerId; + private boolean mEnableDivider; + private boolean mDisableHeaderClick; + private Drawable mDrawable; + // 标签父布局的左间距 + private int mParentPaddingLeft; + // RecyclerView的左间距 + private int mRecyclerViewPaddingLeft; + // 标签父布局的顶间距 + private int mParentPaddingTop; + // RecyclerView的顶间距 + private int mRecyclerViewPaddingTop; + private int mHeaderLeftMargin; + private int mHeaderRightMargin; + private int mHeaderTopMargin; + private int mHeaderBottomMargin; + private OnItemTouchListener mItemTouchListener; + private int mLeft; + private int mTop; + private int mRight; + private int mBottom; + private View mPinnedHeaderParentView; + + // 缓存某个标签 + private View mPinnedHeaderView = null; + + // 缓存某个标签的位置 + private int mHeaderPosition = -1; + + // 顶部标签的Y轴偏移值 + private int mPinnedHeaderOffset; + // 用于锁定画布绘制范围 + private Rect mClipBounds; + + private int mFirstVisiblePosition; + private int mDataPositionOffset; + + private int mPinnedHeaderType; + + private boolean mDisableDrawHeader; + + private RecyclerView mParent; + + private SmallPinnedHeaderItemDecoration(Builder builder) { + mEnableDivider = builder.enableDivider; + mHeaderClickListener = builder.headerClickListener; + mDividerId = builder.dividerId; + mPinnedHeaderId = builder.pinnedHeaderId; + mClickIds = builder.clickIds; + mDisableHeaderClick = builder.disableHeaderClick; + mPinnedHeaderType = builder.pinnedHeaderType; + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + + checkCache(parent); + + if (!mEnableDivider) { + return; + } + + if (mDrawable == null) { + mDrawable = ContextCompat.getDrawable(parent.getContext(), mDividerId != 0 ? mDividerId : R.drawable.divider); + } + + outRect.set(0, 0, 0, mDrawable.getIntrinsicHeight()); + + } + + @Override + public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + + if (mEnableDivider) { + drawDivider(c, parent); + } + + // 只支持LinearLayoutManager或者GridLayoutManager且一行只有一列的情况,这个比较符合使用场景 + if (parent.getLayoutManager() instanceof GridLayoutManager && ((GridLayoutManager) parent.getLayoutManager()).getSpanCount() > 1) { + return; + } else if (parent.getLayoutManager() instanceof StaggeredGridLayoutManager) { + return; + } + + // 检测到标签存在的时候,将标签强制固定在RecyclerView顶部 + createPinnedHeader(parent); + + if (!mDisableDrawHeader && mPinnedHeaderView != null && mFirstVisiblePosition >= mHeaderPosition) { + // 标签相对parent高度加上自身的高度 + final int headerEndAt = mPinnedHeaderParentView.getTop() + mPinnedHeaderParentView.getMeasuredHeight() + mRecyclerViewPaddingTop; + // 根据xy坐标查找view + View v = parent.findChildViewUnder(c.getWidth() / 2, headerEndAt + 1); + if (isPinnedHeader(parent, v) && v.getTop() <= mPinnedHeaderView.getHeight() + mRecyclerViewPaddingTop + mParentPaddingTop) { + // 如果view是标签的话,那么缓存的标签就要跟随这个真正的标签标签移动了,效果类似于下面的标签把它顶上去一样 + // 得到mPinnedHeaderView为标签跟随移动的位移 + mPinnedHeaderOffset = v.getTop() - (mRecyclerViewPaddingTop + mParentPaddingTop + mPinnedHeaderView.getHeight()); + } else { + mPinnedHeaderOffset = 0; + } + + // Log.e("TAG", "SmallPinnedHeaderItemDecoration-152行-onDraw(): " + v.getTop() + ";" + mPinnedHeaderOffset); + + // 拿到锁定的矩形 + mClipBounds = c.getClipBounds(); + + mClipBounds.left = 0; + mClipBounds.right = parent.getWidth(); + mClipBounds.top = mRecyclerViewPaddingTop + mParentPaddingTop; + mClipBounds.bottom = parent.getHeight(); + + // 重新锁定 + c.clipRect(mClipBounds); + + } + } + + private void drawDivider(Canvas c, RecyclerView parent) { + + if (mAdapter == null) { + // checkCache的话RecyclerView未设置之前mAdapter为空 + return; + } + + // 不让分割线画出界限 + c.clipRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getWidth() - parent.getPaddingRight(), parent.getHeight() - parent.getPaddingBottom()); + + int childCount = parent.getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = parent.getChildAt(i); + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + DividerHelper.drawBottomAlignItem(c, mDrawable, child, params); + } + } + + @Override + public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { + + if (!mDisableDrawHeader && mPinnedHeaderView != null && mFirstVisiblePosition >= mHeaderPosition) { + + c.save(); + + mClipBounds.left = mRecyclerViewPaddingLeft + mParentPaddingLeft + mHeaderLeftMargin; + mClipBounds.right = mClipBounds.left + mPinnedHeaderView.getWidth(); + mClipBounds.top = mRecyclerViewPaddingTop + mParentPaddingTop + mHeaderTopMargin; + mClipBounds.bottom = mPinnedHeaderOffset + mPinnedHeaderView.getHeight() + mClipBounds.top; + + if (mItemTouchListener != null) { + mItemTouchListener.invalidTopAndBottom(mPinnedHeaderOffset); + } + + // 取AB交集这个就是标签绘制的范围了 + c.clipRect(mClipBounds, Region.Op.INTERSECT); + c.translate(mRecyclerViewPaddingLeft + mParentPaddingLeft + mHeaderLeftMargin, + mPinnedHeaderOffset + mRecyclerViewPaddingTop + mParentPaddingTop + mHeaderTopMargin); + mPinnedHeaderView.draw(c); + + c.restore(); + } else if (mItemTouchListener != null) { + // 不绘制的时候,把头部的偏移值偏移用户点击不到的程度 + mItemTouchListener.invalidTopAndBottom(-1000); + } + } + + // 创建标签 + @SuppressWarnings("unchecked") + private void createPinnedHeader(RecyclerView parent) { + + if (mAdapter == null) { + // checkCache的话RecyclerView未设置之前mAdapter为空 + return; + } + + final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); + + // 获取第一个可见的item位置 + mFirstVisiblePosition = 0; + int headerPosition; + + if (layoutManager instanceof GridLayoutManager) { + mFirstVisiblePosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition(); + } else if (layoutManager instanceof LinearLayoutManager) { + mFirstVisiblePosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); + } + + // 通过第一个部分可见的item位置获取标签的位置 + headerPosition = findPinnedHeaderPosition(mFirstVisiblePosition); + + if (headerPosition >= 0 && mHeaderPosition != headerPosition) { + + // Log.e("TAG", "创建标签"); + + // 缓存位置 + mHeaderPosition = headerPosition; + // 获取此位置的type + final int viewType = mAdapter.getItemViewType(headerPosition); + + // 手动调用创建出标签 + final RecyclerView.ViewHolder pinnedViewHolder = mAdapter.createViewHolder(parent, viewType); + mAdapter.bindViewHolder(pinnedViewHolder, headerPosition); + + mPinnedHeaderParentView = pinnedViewHolder.itemView; + + measurePinedHeaderParent(parent); + + measurePinnedHeader(); + + mLeft = mRecyclerViewPaddingLeft + mParentPaddingLeft + mHeaderLeftMargin; + mRight = mPinnedHeaderView.getMeasuredWidth() + mLeft; + mTop = mRecyclerViewPaddingTop + mParentPaddingTop + mHeaderTopMargin; + mBottom = mPinnedHeaderView.getMeasuredHeight() + mTop; + + // 位置强制布局在顶部 + mPinnedHeaderView.layout(mLeft, mTop, mRight, mBottom); + + if (mItemTouchListener == null && mHeaderClickListener != null) { + mItemTouchListener = new OnItemTouchListener(parent.getContext()); + try { + final Field field = parent.getClass().getDeclaredField("mOnItemTouchListeners"); + field.setAccessible(true); + final ArrayList touchListeners = (ArrayList) field.get(parent); + touchListeners.add(0, mItemTouchListener); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + parent.addOnItemTouchListener(mItemTouchListener); + } catch (IllegalAccessException e) { + e.printStackTrace(); + parent.addOnItemTouchListener(mItemTouchListener); + } + mItemTouchListener.setHeaderClickListener(mHeaderClickListener); + mItemTouchListener.disableHeaderClick(mDisableHeaderClick); + mItemTouchListener.setClickBounds(OnItemTouchListener.HEADER_ID, mPinnedHeaderView); + } + if (mHeaderClickListener != null) { + // -1代表是标签的Id + mItemTouchListener.setClickBounds(OnItemTouchListener.HEADER_ID, mPinnedHeaderView); + if (mHeaderClickListener != null && mClickIds != null && mClickIds.length > 0) { + for (int mClickId : mClickIds) { + final View view = mPinnedHeaderView.findViewById(mClickId); + if (view != null && view.getVisibility() == View.VISIBLE) { + mItemTouchListener.setClickBounds(mClickId, view); + } + } + } + mItemTouchListener.setClickHeaderInfo(mHeaderPosition - mDataPositionOffset); + } + + } + } + + public int getDataPositionOffset() { + return mDataPositionOffset; + } + + public void setDataPositionOffset(int offset) { + mDataPositionOffset = offset; + } + + // 测量标签父布局的宽高 + private void measurePinedHeaderParent(RecyclerView parent) { + // 1.测量标签的parent + ViewGroup.LayoutParams parentLp = mPinnedHeaderParentView.getLayoutParams(); + if (parentLp == null) { + parentLp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + mPinnedHeaderParentView.setLayoutParams(parentLp); + } + int heightMode = View.MeasureSpec.getMode(ViewGroup.LayoutParams.WRAP_CONTENT); + int heightSize = View.MeasureSpec.getSize(parentLp.height); + + switch (heightMode) { + case View.MeasureSpec.UNSPECIFIED: + heightMode = View.MeasureSpec.EXACTLY; + break; + case View.MeasureSpec.EXACTLY: + heightMode = View.MeasureSpec.EXACTLY; + break; + case View.MeasureSpec.AT_MOST: + default: + heightMode = View.MeasureSpec.AT_MOST; + break; + } + int maxHeight = parent.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom(); + heightSize = Math.min(heightSize, maxHeight); + int ws = View.MeasureSpec.makeMeasureSpec(parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.EXACTLY); + int hs = View.MeasureSpec.makeMeasureSpec(heightSize, heightMode); + // 强制测量 + mPinnedHeaderParentView.measure(ws, hs); + + mRecyclerViewPaddingLeft = parent.getPaddingLeft(); + mParentPaddingLeft = mPinnedHeaderParentView.getPaddingLeft(); + + mRecyclerViewPaddingTop = parent.getPaddingTop(); + mParentPaddingTop = mPinnedHeaderParentView.getPaddingTop(); + + if (parentLp instanceof RecyclerView.LayoutParams) { + mRecyclerViewPaddingLeft += ((RecyclerView.LayoutParams) parentLp).leftMargin; + mRecyclerViewPaddingTop += ((RecyclerView.LayoutParams) parentLp).topMargin; + } + } + + // 测量标签高度 + private void measurePinnedHeader() { + + // 2.测量标签 + mPinnedHeaderView = mPinnedHeaderParentView.findViewById(mPinnedHeaderId); + // 获取标签的布局属性 + ViewGroup.LayoutParams lp = mPinnedHeaderView.getLayoutParams(); + if (lp == null) { + lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + mPinnedHeaderView.setLayoutParams(lp); + } + + if (lp instanceof ViewGroup.MarginLayoutParams) { + final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp; + mHeaderLeftMargin = mlp.leftMargin; + mHeaderRightMargin = mlp.rightMargin; + mHeaderTopMargin = mlp.topMargin; + mHeaderBottomMargin = mlp.bottomMargin; + } + + // 设置高度 + int heightMode = View.MeasureSpec.getMode(lp.height); + int heightSize = View.MeasureSpec.getSize(lp.height); + + switch (heightMode) { + case View.MeasureSpec.UNSPECIFIED: + heightMode = View.MeasureSpec.EXACTLY; + break; + case View.MeasureSpec.EXACTLY: + heightMode = View.MeasureSpec.EXACTLY; + break; + case View.MeasureSpec.AT_MOST: + heightMode = View.MeasureSpec.AT_MOST; + break; + default: + heightMode = View.MeasureSpec.AT_MOST; + break; + } + + // 最大高度为mPinnedHeaderParentView的高度减去padding + int maxHeight = mPinnedHeaderParentView.getMeasuredHeight() - mPinnedHeaderParentView.getPaddingTop() - mPinnedHeaderParentView.getPaddingBottom(); + heightSize = Math.min(heightSize, maxHeight); + + int hs = View.MeasureSpec.makeMeasureSpec(heightSize, heightMode); + + // 设置宽度 + int widthMode = View.MeasureSpec.getMode(lp.width); + int widthSize = View.MeasureSpec.getSize(lp.width); + + switch (widthMode) { + case View.MeasureSpec.UNSPECIFIED: + widthMode = View.MeasureSpec.EXACTLY; + break; + case View.MeasureSpec.EXACTLY: + widthMode = View.MeasureSpec.EXACTLY; + break; + case View.MeasureSpec.AT_MOST: + widthMode = View.MeasureSpec.AT_MOST; + break; + default: + widthMode = View.MeasureSpec.AT_MOST; + break; + } + + int maxWidth = mPinnedHeaderParentView.getMeasuredWidth() - mPinnedHeaderParentView.getPaddingLeft() - mPinnedHeaderParentView.getPaddingRight(); + widthSize = Math.min(widthSize, maxWidth); + + int ws = View.MeasureSpec.makeMeasureSpec(widthSize, widthMode); + + // 强制测量 + mPinnedHeaderView.measure(ws, hs); + + } + + // 查找标签的位置 + private int findPinnedHeaderPosition(int fromPosition) { + + for (int position = fromPosition; position >= 0; position--) { + // 从这个位置开始递减,只要一查到有位置type为标签,立即返回此标签位置 + final int viewType = mAdapter.getItemViewType(position); + // 检查是否是标签类型 + if (isPinnedViewType(viewType)) { + // 是标签类型,返回位置 + return position; + } + } + + return -1; + } + + // 检查传入View是否是标签 + private boolean isPinnedHeader(RecyclerView parent, View v) { + // 获取View在parent中的位置 + final int position = parent.getChildAdapterPosition(v); + if (position == RecyclerView.NO_POSITION) { + return false; + } + // 获取View的type + final int viewType = mAdapter.getItemViewType(position); + + // 检查是否是标签类型 + return isPinnedViewType(viewType); + } + + // 检查是否是标签类型 + private boolean isPinnedViewType(int viewType) { + return viewType == mPinnedHeaderType; + } + + // 检查缓存 + private void checkCache(RecyclerView parent) { + // 取出RecyclerView的适配器 + + if (mParent != parent) { + mParent = parent; + } + + RecyclerView.Adapter adapter = parent.getAdapter(); + if (mAdapter != adapter) { + // 适配器有差异,清空缓存 + mPinnedHeaderView = null; + mHeaderPosition = -1; + mAdapter = adapter; + mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + super.onChanged(); + reset(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + super.onItemRangeChanged(positionStart, itemCount); + reset(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { + super.onItemRangeChanged(positionStart, itemCount, payload); + reset(); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + super.onItemRangeInserted(positionStart, itemCount); + reset(); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + super.onItemRangeRemoved(positionStart, itemCount); + reset(); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + super.onItemRangeMoved(fromPosition, toPosition, itemCount); + reset(); + } + }); + } + } + + private void reset() { + mHeaderPosition = -1; + mPinnedHeaderView = null; + } + + public View getPinnedHeaderView() { + return mPinnedHeaderView; + } + + public int getPinnedHeaderPosition() { + return mHeaderPosition; + } + + /** + * 是否禁止绘制粘性头部 + * + * @return true的话不绘制头部 + */ + public boolean isDisableDrawHeader() { + return mDisableDrawHeader; + } + + /** + * 禁止绘制粘性头部 + * + * @param disableDrawHeader true的话不绘制头部,默认false绘制头部 + */ + public void disableDrawHeader(boolean disableDrawHeader) { + mDisableDrawHeader = disableDrawHeader; + if (mParent != null) { + mParent.invalidateItemDecorations(); + } + } + + public static class Builder { + + public boolean disableHeaderClick; + private OnHeaderClickListener headerClickListener; + private int dividerId; + private int pinnedHeaderId; + private boolean enableDivider; + private int[] clickIds; + + private int pinnedHeaderType; + + /** + * 构造方法 + * + * @param pinnedHeaderId 小标签对应的ID + * @param pinnedHeaderType 粘性标签的类型 + */ + public Builder(int pinnedHeaderId, int pinnedHeaderType) { + this.pinnedHeaderId = pinnedHeaderId; + this.pinnedHeaderType = pinnedHeaderType; + } + + /** + * 设置标签和其内部的子控件的监听,若设置点击监听不为null,但是disableHeaderClick(true)的话,还是不会响应点击事件 + * + * @param headerClickListener 监听,若不设置这个setClickIds无效 + * @return 构建者 + */ + public Builder setHeaderClickListener(OnHeaderClickListener headerClickListener) { + this.headerClickListener = headerClickListener; + return this; + } + + /** + * 设置分隔线资源ID + * + * @param dividerId 资源ID,若不设置这个并且enableDivider=true时,使用默认的分隔线 + * @return 构建者 + */ + public Builder setDividerId(int dividerId) { + this.dividerId = dividerId; + return this; + } + + /** + * 是否开启绘制分隔线,默认关闭 + * + * @param enableDivider true为绘制,false不绘制,false时setDividerId无效 + * @return 构建者 + */ + public Builder enableDivider(boolean enableDivider) { + this.enableDivider = enableDivider; + return this; + } + + /** + * 通过传入包括标签和其内部的子控件的ID设置其对应的点击事件 + * + * @param clickIds 标签或其内部的子控件的ID + * @return 构建者 + */ + public Builder setClickIds(int... clickIds) { + this.clickIds = clickIds; + return this; + } + + /** + * 是否关闭标签点击事件,默认开启 + * + * @param disableHeaderClick true为关闭标签点击事件,false为开启标签点击事件 + * @return 构建者 + */ + public Builder disableHeaderClick(boolean disableHeaderClick) { + this.disableHeaderClick = disableHeaderClick; + return this; + } + + public SmallPinnedHeaderItemDecoration create() { + return new SmallPinnedHeaderItemDecoration(this); + } + } + +} diff --git a/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/callback/OnHeaderClickAdapter.java b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/callback/OnHeaderClickAdapter.java new file mode 100644 index 0000000..ef1f86a --- /dev/null +++ b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/callback/OnHeaderClickAdapter.java @@ -0,0 +1,25 @@ +package com.xty.common.weight.pinnedsectionitemdecoration.callback; + +import android.view.View; + +/** + * Created by Oubowu on 2016/7/25 1:02. + */ +public class OnHeaderClickAdapter implements OnHeaderClickListener { + + + @Override + public void onHeaderClick(View view, int id, int position) { + + } + + @Override + public void onHeaderLongClick(View view, int id, int position) { + + } + +// @Override +// public void onHeaderDoubleClick(View view, int id, int position) { +// +// } +} diff --git a/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/callback/OnHeaderClickListener.java b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/callback/OnHeaderClickListener.java new file mode 100644 index 0000000..a1e6c80 --- /dev/null +++ b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/callback/OnHeaderClickListener.java @@ -0,0 +1,17 @@ +package com.xty.common.weight.pinnedsectionitemdecoration.callback; + +import android.view.View; + +/** + * Created by Oubowu on 2016/7/24 23:53. + *

顶部标签点击监听

+ */ +public interface OnHeaderClickListener { + + void onHeaderClick(View view, int id, int position); + + void onHeaderLongClick(View view, int id, int position); + +// void onHeaderDoubleClick(View view, int id, int position); + +} diff --git a/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/callback/OnItemTouchListener.java b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/callback/OnItemTouchListener.java new file mode 100644 index 0000000..7b07b54 --- /dev/null +++ b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/callback/OnItemTouchListener.java @@ -0,0 +1,252 @@ +package com.xty.common.weight.pinnedsectionitemdecoration.callback; + +import android.content.Context; + +import android.util.Log; +import android.util.SparseArray; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.xty.common.weight.pinnedsectionitemdecoration.entity.ClickBounds; + + +/** + * Created by Oubowu on 2016/7/24 20:51. + *

用来处理标签的点击事件,现在仅仅支持单击,将来也许会实现长按和双击事件

+ */ +public class OnItemTouchListener implements RecyclerView.OnItemTouchListener { + private static final String TAG = "OnItemTouchListener"; + /** + * 代表的是标签的Id + */ + public static final int HEADER_ID = -1; + + private ClickBounds mTmpBounds; + + private View mTmpView; + + private int mTmpClickId; + + private GestureDetector mGestureDetector; + + private SparseArray mBoundsArray; + + private boolean mIntercept; + + private OnHeaderClickListener mHeaderClickListener; + + private int mPosition; + + private boolean mDisableHeaderClick; + // private boolean mDownInside; + private RecyclerView.Adapter mAdapter; + private RecyclerView mRecyclerView; + + public OnItemTouchListener(Context context) { + + mBoundsArray = new SparseArray<>(); + + GestureListener gestureListener = new GestureListener(); + mGestureDetector = new GestureDetector(context, gestureListener); + } + + /** + * 设置对应的View的点击范围 + * + * @param id View的ID + * @param bounds 点击范围 + */ + @Deprecated + public void setClickBounds(int id, ClickBounds bounds) { + mBoundsArray.put(id, bounds); + } + + /** + * 设置对应的View的点击范围 + * + * @param id View的ID + * @param view 点击的View + */ + public void setClickBounds(int id, View view) { + ClickBounds bounds; + if (mBoundsArray.get(id) == null) { + bounds = new ClickBounds(view, view.getLeft(), view.getTop(), view.getLeft() + view.getMeasuredWidth(), view.getTop() + view.getMeasuredHeight()); + mBoundsArray.put(id, bounds); + } else { + bounds = mBoundsArray.get(id); + bounds.setBounds(view.getLeft(), view.getTop(), view.getLeft() + view.getMeasuredWidth(), view.getTop() + view.getMeasuredHeight()); + } + } + + /** + * 更新点击范围的顶部和底部 + * + * @param offset 偏差 + */ + public void invalidTopAndBottom(int offset) { + for (int i = 0; i < mBoundsArray.size(); i++) { + final ClickBounds bounds = mBoundsArray.valueAt(i); + bounds.setTop(bounds.getFirstTop() + offset); + bounds.setBottom(bounds.getFirstBottom() + offset); + } + } + + @Override + public boolean onInterceptTouchEvent(@NonNull final RecyclerView rv, @NonNull MotionEvent event) { + + if (mRecyclerView != rv) { + mRecyclerView = rv; + } + if (mAdapter != rv.getAdapter()) { + mAdapter = rv.getAdapter(); + } + + // 这里处理触摸事件来决定是否自己处理事件 + mGestureDetector.setIsLongpressEnabled(true); + mGestureDetector.onTouchEvent(event); + + return mIntercept; + } + + @Override + public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) { + Log.i(TAG, "onTouchEvent(): " + e.toString()); + mGestureDetector.onTouchEvent(e); + } + + @Override + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { + } + + public void setClickHeaderInfo(int position) { + mPosition = position; + } + + public void setHeaderClickListener(OnHeaderClickListener headerClickListener) { + mHeaderClickListener = headerClickListener; + } + + public void disableHeaderClick(boolean disableHeaderClick) { + mDisableHeaderClick = disableHeaderClick; + } + + private void shouldIntercept(MotionEvent e) { + float downX = e.getX(); + float downY = e.getY(); + + // 如果坐标在标签的范围内的话就屏蔽事件,自己处理 + // mIntercept = downX >= mLeft && downX <= mRight && downY >= mTop && downY <= mBottom; + + // Log.i(TAG, " xy坐标: " + downX + ";" + downY); + + for (int i = 0; i < mBoundsArray.size(); i++) { + // 逐个View拿出,判断坐标是否落在View的范围里面 + final ClickBounds bounds = mBoundsArray.valueAt(i); + // Log.i(TAG, " 逐个View拿出: " + bounds.toString()); + + boolean inside = downX >= bounds.getLeft() && downX <= bounds.getRight() && downY >= bounds.getTop() && downY <= bounds.getBottom(); + if (inside) { + // Log.i(TAG, " 在点击范围内: " + inside); + // 拦截事件成立 + mIntercept = true; + // 点击范围内 + if (mTmpBounds == null) { + mTmpBounds = bounds; + } else if (bounds.getLeft() >= mTmpBounds.getLeft() && bounds.getRight() <= mTmpBounds.getRight() && bounds.getTop() >= mTmpBounds.getTop() && bounds + .getBottom() <= mTmpBounds.getBottom()) { + // 与缓存的在点击范围的进行比较,若其点击范围比缓存的更小,它点击响应优先级更高 + mTmpBounds = bounds; + } + } else if (mTmpBounds == null) { + mIntercept = false; + } + } + + if (mIntercept) { + // 有点击中的,取出其id并清空mTmpBounds + mTmpClickId = mBoundsArray.keyAt(mBoundsArray.indexOfValue(mTmpBounds)); + mTmpView = mTmpBounds.getView(); + mTmpBounds = null; + // Log.i(TAG, " 有点击中的: " + mTmpView); + } + + // Log.i(TAG, "OnRecyclerItemTouchListener-judge(): " + (mIntercept ? "屏蔽" : "不屏蔽")); + + } + + private class GestureListener extends GestureDetector.SimpleOnGestureListener { + + @Override + public boolean onDown(MotionEvent e) { + + Log.i(TAG, "GestureListener-onDown(): "); + + shouldIntercept(e); + + return super.onDown(e); + } + + @Override + public void onLongPress(MotionEvent e) { + Log.i(TAG, "GestureListener-onLongPress(): "); + shouldIntercept(e); + + if (!mDisableHeaderClick && mIntercept && mHeaderClickListener != null && mAdapter != null && mPosition <= mAdapter.getItemCount() - 1) { + // 自己处理点击标签事件 + try { + mHeaderClickListener.onHeaderLongClick(mTmpView, mTmpClickId, mPosition); + } catch (IndexOutOfBoundsException e1) { + e1.printStackTrace(); + Log.i(TAG, "GestureListener-onLongPress(): " + e1); + } + } + + } + + @Override + public boolean onSingleTapUp(MotionEvent e) { + Log.i(TAG, "GestureListener-onSingleTapUp(): "); + shouldIntercept(e); + + if (!mDisableHeaderClick && mIntercept && mHeaderClickListener != null && mAdapter != null && mPosition <= mAdapter.getItemCount() - 1) { + // 自己处理点击标签事件 + try { + mHeaderClickListener.onHeaderClick(mTmpView, mTmpClickId, mPosition); + } catch (IndexOutOfBoundsException e1) { + e1.printStackTrace(); + } + } + + return mIntercept; + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + + Log.i(TAG, "GestureListener-onDoubleTap(): "); + + shouldIntercept(e); + + if (!mDisableHeaderClick && mIntercept && mHeaderClickListener != null && mAdapter != null && mPosition <= mAdapter.getItemCount() - 1) { + // 自己处理点击标签事件 + try { + mHeaderClickListener.onHeaderClick(mTmpView, mTmpClickId, mPosition); + } catch (IndexOutOfBoundsException e1) { + e1.printStackTrace(); + } + } + + // 有机型在调用onDoubleTap后会接着调用onLongPress,这里这样处理 + mGestureDetector.setIsLongpressEnabled(false); + + return mIntercept; + + } + + } + +} diff --git a/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/entity/ClickBounds.java b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/entity/ClickBounds.java new file mode 100644 index 0000000..08b5ceb --- /dev/null +++ b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/entity/ClickBounds.java @@ -0,0 +1,86 @@ +package com.xty.common.weight.pinnedsectionitemdecoration.entity; + +import android.view.View; + +/** + * Created by Oubowu on 2016/7/27 23:52. + *

点击范围实体类,用于点击标签时做点击判断

+ */ +public class ClickBounds { + + private View mView; + + private int mLeft; + private int mTop; + private int mRight; + private int mBottom; + + // 记录第一次Top和Bottom,用于后面减偏差 + private int mFirstTop; + private int mFirstBottom; + + public ClickBounds(View view, int left, int top, int right, int bottom) { + mView = view; + mLeft = left; + mTop = top; + mRight = right; + mBottom = bottom; + + mFirstTop = top; + mFirstBottom = bottom; + } + + public void setBounds(int left, int top, int right, int bottom) { + mLeft = left; + mTop = top; + mRight = right; + mBottom = bottom; + + mFirstTop = top; + mFirstBottom = bottom; + } + + public int getLeft() { + return mLeft; + } + + public int getTop() { + return mTop; + } + + public int getRight() { + return mRight; + } + + public int getBottom() { + return mBottom; + } + + public void setBottom(int bottom) { + mBottom = bottom; + } + + public void setLeft(int left) { + mLeft = left; + } + + public void setTop(int top) { + mTop = top; + } + + public void setRight(int right) { + mRight = right; + } + + public int getFirstBottom() { + return mFirstBottom; + } + + public int getFirstTop() { + return mFirstTop; + } + + public View getView() { + return mView; + } +} diff --git a/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/utils/DividerHelper.java b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/utils/DividerHelper.java new file mode 100644 index 0000000..1156f21 --- /dev/null +++ b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/utils/DividerHelper.java @@ -0,0 +1,114 @@ +package com.xty.common.weight.pinnedsectionitemdecoration.utils; + +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.view.ViewGroup; + +/** + * Created by Oubowu on 2016/7/26 14:14. + *

绘制分割线工具类

+ */ +public class DividerHelper { + + // 将分割线画在view的顶部,并且左右会多出分割线的宽度 + public static void drawTop(Canvas canvas, Drawable drawable, View child, ViewGroup.MarginLayoutParams params) { + + final int left = child.getLeft() - params.leftMargin - drawable.getIntrinsicWidth(); + final int right = child.getRight() + params.rightMargin + drawable.getIntrinsicWidth(); + final int top = child.getTop() - params.topMargin - drawable.getIntrinsicHeight(); + final int bottom = top + drawable.getIntrinsicHeight(); + + drawable.setBounds(left, top, right, bottom); + drawable.draw(canvas); + } + + // 将分割线画在view的底部,并且左右会多出分割线的宽度 + public static void drawBottom(Canvas canvas, Drawable drawable, View child, ViewGroup.MarginLayoutParams params) { + + final int left = child.getLeft() - params.leftMargin - drawable.getIntrinsicWidth(); + final int right = child.getRight() + params.rightMargin + drawable.getIntrinsicWidth(); + final int top = child.getBottom() + params.bottomMargin; + final int bottom = top + drawable.getIntrinsicHeight(); + + drawable.setBounds(left, top, right, bottom); + drawable.draw(canvas); + } + + // 将分割线画在view的左边,并且上下会多出分割线的高度 + public static void drawLeft(Canvas canvas, Drawable drawable, View child, ViewGroup.MarginLayoutParams params) { + + final int top = child.getTop() - params.topMargin - drawable.getIntrinsicHeight(); + final int bottom = child.getBottom() + params.bottomMargin + drawable.getIntrinsicHeight(); + final int left = child.getLeft() - params.leftMargin - drawable.getIntrinsicWidth(); + final int right = left + drawable.getIntrinsicWidth(); + + drawable.setBounds(left, top, right, bottom); + + drawable.draw(canvas); + } + + // 将分割线画在view的右边,并且上下会多出分割线的高度 + public static void drawRight(Canvas canvas, Drawable drawable, View child, ViewGroup.MarginLayoutParams params) { + + final int top = child.getTop() - params.topMargin - drawable.getIntrinsicHeight(); + final int bottom = child.getBottom() + params.bottomMargin + drawable.getIntrinsicHeight(); + final int left = child.getRight() + params.rightMargin; + final int right = left + drawable.getIntrinsicWidth(); + + drawable.setBounds(left, top, right, bottom); + drawable.draw(canvas); + } + + + // 将分割线画在view的顶部,与view左右对齐,考虑margin值 + public static void drawTopAlignItem(Canvas canvas, Drawable drawable, View child, ViewGroup.MarginLayoutParams params) { + + final int left = child.getLeft() - params.leftMargin; + final int right = child.getRight() + params.rightMargin; + final int top = child.getTop() - params.topMargin - drawable.getIntrinsicHeight(); + final int bottom = top + drawable.getIntrinsicHeight(); + + drawable.setBounds(left, top, right, bottom); + drawable.draw(canvas); + } + + // 将分割线画在view的底部,与view左右对齐,考虑margin值 + public static void drawBottomAlignItem(Canvas canvas, Drawable drawable, View child, ViewGroup.MarginLayoutParams params) { + + final int left = child.getLeft() - params.leftMargin; + final int right = child.getRight() + params.rightMargin; + final int top = child.getBottom() + params.bottomMargin; + final int bottom = top + drawable.getIntrinsicHeight(); + + drawable.setBounds(left, top, right, bottom); + drawable.draw(canvas); + } + + // 将分割线画在view的左边,与Item上下对齐,考虑margin值 + public static void drawLeftAlignItem(Canvas canvas, Drawable drawable, View child, ViewGroup.MarginLayoutParams params) { + + final int top = child.getTop() - params.topMargin; + final int bottom = child.getBottom() + params.bottomMargin; + final int left = child.getLeft() - params.leftMargin - drawable.getIntrinsicWidth(); + final int right = left + drawable.getIntrinsicWidth(); + + drawable.setBounds(left, top, right, bottom); + + drawable.draw(canvas); + } + + // 将分割线画在view的右边,与Item上下对齐,考虑margin值 + public static void drawRightAlignItem(Canvas canvas, Drawable drawable, View child, ViewGroup.MarginLayoutParams params) { + + final int top = child.getTop() - params.topMargin; + final int bottom = child.getBottom() + params.bottomMargin; + final int left = child.getRight() + params.rightMargin; + final int right = left + drawable.getIntrinsicWidth(); + + drawable.setBounds(left, top, right, bottom); + drawable.draw(canvas); + } + + +} diff --git a/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/utils/FullSpanUtil.java b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/utils/FullSpanUtil.java new file mode 100644 index 0000000..ba46a0a --- /dev/null +++ b/common/src/main/java/com/xty/common/weight/pinnedsectionitemdecoration/utils/FullSpanUtil.java @@ -0,0 +1,46 @@ +package com.xty.common.weight.pinnedsectionitemdecoration.utils; + + +import android.view.ViewGroup; + +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.StaggeredGridLayoutManager; + +/** + * Created by Oubowu on 2016/7/29 17:28. + */ +public class FullSpanUtil { + + public static void onAttachedToRecyclerView(RecyclerView recyclerView, final RecyclerView.Adapter adapter, final int pinnedHeaderType) { + // 如果是网格布局,这里处理标签的布局占满一行 + final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); + if (layoutManager instanceof GridLayoutManager) { + final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager; + final GridLayoutManager.SpanSizeLookup oldSizeLookup = gridLayoutManager.getSpanSizeLookup(); + gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + if (adapter.getItemViewType(position) == pinnedHeaderType) { + return gridLayoutManager.getSpanCount(); + } + if (oldSizeLookup != null) { + return oldSizeLookup.getSpanSize(position); + } + return 1; + } + }); + } + } + + public static void onViewAttachedToWindow(RecyclerView.ViewHolder holder, RecyclerView.Adapter adapter, int pinnedHeaderType) { + // 如果是瀑布流布局,这里处理标签的布局占满一行 + final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); + if (lp instanceof StaggeredGridLayoutManager.LayoutParams) { + final StaggeredGridLayoutManager.LayoutParams slp = (StaggeredGridLayoutManager.LayoutParams) lp; + slp.setFullSpan(adapter.getItemViewType(holder.getLayoutPosition()) == pinnedHeaderType); + } + } + + +} diff --git a/common/src/main/res/drawable/divider.xml b/common/src/main/res/drawable/divider.xml new file mode 100644 index 0000000..834d033 --- /dev/null +++ b/common/src/main/res/drawable/divider.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/health/src/main/java/com/zj365/health/act/healthcode/HealthBodyInquiryAct.kt b/health/src/main/java/com/zj365/health/act/healthcode/HealthBodyInquiryAct.kt index 669a868..6a13bf6 100644 --- a/health/src/main/java/com/zj365/health/act/healthcode/HealthBodyInquiryAct.kt +++ b/health/src/main/java/com/zj365/health/act/healthcode/HealthBodyInquiryAct.kt @@ -1,5 +1,7 @@ package com.zj365.health.act.healthcode +import android.text.Html +import android.text.TextUtils import android.view.View import com.alibaba.android.arouter.facade.annotation.Route import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram @@ -21,10 +23,38 @@ class HealthBodyInquiryAct : BaseVmAct() { override fun liveObserver() { mViewModel.inquiryDetailLiveData.observe(this){ binding.title.mTvTitle.text = "${it.data.ref_name}" - binding.tvDepartment.text = it.data.department - binding.tvSurvey.text = it.data.condition - binding.tvDefinition.text = it.data.seek - binding.tvPathogeny.text = it.data.etiology + if (TextUtils.isEmpty(it.data.department)){ + binding.llDepartment.visibility = View.GONE + }else{ + binding.llDepartment.visibility = View.VISIBLE + binding.tvDepartment.text = Html.fromHtml(it.data.department) + + } + + if(TextUtils.isEmpty(it.data.condition)){ + binding.llSurvey.visibility = View.GONE + }else{ + binding.llSurvey.visibility = View.VISIBLE + binding.tvSurvey.text = Html.fromHtml(it.data.condition) + + } + + if(TextUtils.isEmpty(it.data.symptom)){ + binding.llDefinition.visibility = View.GONE + }else{ + binding.llDefinition.visibility = View.VISIBLE + binding.tvDefinition.text = Html.fromHtml(it.data.symptom) + + } + + if (TextUtils.isEmpty(it.data.etiology)){ + binding.llPathogeny.visibility = View.GONE + }else{ + binding.llPathogeny.visibility = View.VISIBLE + binding.tvPathogeny.text = Html.fromHtml(it.data.etiology) + + } + } diff --git a/health/src/main/java/com/zj365/health/act/healthcode/HealthBodyPartAct.kt b/health/src/main/java/com/zj365/health/act/healthcode/HealthBodyPartAct.kt index d16f547..fc04dfa 100644 --- a/health/src/main/java/com/zj365/health/act/healthcode/HealthBodyPartAct.kt +++ b/health/src/main/java/com/zj365/health/act/healthcode/HealthBodyPartAct.kt @@ -1,21 +1,18 @@ package com.zj365.health.act.healthcode -import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import com.alibaba.android.arouter.facade.annotation.Route import com.xty.base.act.BaseVmAct -import com.xty.base.vm.BaseVm import com.xty.common.arouter.ARouterUrl import com.xty.common.arouter.RouteManager -import com.xty.common.weight.PinnedHeaderItemDecoration import com.xty.common.weight.SideBar +import com.xty.common.weight.pinnedsectionitemdecoration.PinnedHeaderItemDecoration import com.xty.network.model.HealthBodyPartBean import com.xty.network.model.HealthHeaderEntity import com.zj365.health.R import com.zj365.health.adapter.healthcode.HealthBodyPartAdapter import com.zj365.health.databinding.ActHealthCodeBodyPartBinding import com.zj365.health.vm.HealthBodyPartVm -import retrofit2.http.Path @Route(path = ARouterUrl.HEALTH_BODY_PART_ACT) class HealthBodyPartAct : BaseVmAct() { @@ -41,7 +38,7 @@ class HealthBodyPartAct : BaseVmAct() { } } - // binding.sideBar.setFilterData(filterSlideList.toArray(arrayOf())) + binding.sideBar.setFilterData(filterSlideList.toArray(arrayOf())) adapter.setNewInstance(mList) } } @@ -60,16 +57,16 @@ class HealthBodyPartAct : BaseVmAct() { mViewModel.getBodyPart(id) - /* val phid = PinnedHeaderItemDecoration.Builder(HealthBodyPartAdapter.TYPE_HEADER) + val phid = PinnedHeaderItemDecoration.Builder(HealthBodyPartAdapter.TYPE_HEADER) .enableDivider(false) .create() - phid.setOnScrollToHeaderListener { + /* phid.setOnScrollToHeaderListener { binding.sideBar.changeCurrentTextColor(it) - } + }*/ binding.recycler!!!!.addItemDecoration( - phid)*/ + phid) binding.sideBar.setOnTouchingLetterChangedListener(SideBar.OnTouchingLetterChangedListener { s: String -> - val position: Int = adapter!!.getPositionForSection(s.get(0)) + val position: Int = adapter!!.getPositionForSection(s[0]) if (position != -1) { binding.recycler!!.layoutManager!!.scrollToPosition(position) } diff --git a/health/src/main/java/com/zj365/health/adapter/FamilyHealth/HealthFamilyAdapter.kt b/health/src/main/java/com/zj365/health/adapter/FamilyHealth/HealthFamilyAdapter.kt index 1e41dbc..a117b63 100644 --- a/health/src/main/java/com/zj365/health/adapter/FamilyHealth/HealthFamilyAdapter.kt +++ b/health/src/main/java/com/zj365/health/adapter/FamilyHealth/HealthFamilyAdapter.kt @@ -78,6 +78,12 @@ class HealthFamilyAdapter: BaseAdapter(R.layout.item_health_fa } + if (item.isBindWatch == 1){ + holder.setGone(R.id.tv_watch,false) + }else{ + holder.setGone(R.id.tv_watch,true) + } + val drawableLeft = context.resources.getDrawable(R.mipmap.icon_power_green) drawableLeft.setBounds(0,0,drawableLeft.minimumWidth,drawableLeft.minimumHeight) diff --git a/health/src/main/res/layout/act_blood_study_needknow.xml b/health/src/main/res/layout/act_blood_study_needknow.xml index 56c020a..cb0bc83 100644 --- a/health/src/main/res/layout/act_blood_study_needknow.xml +++ b/health/src/main/res/layout/act_blood_study_needknow.xml @@ -32,7 +32,7 @@ diff --git a/health/src/main/res/layout/act_health_body_inquiry.xml b/health/src/main/res/layout/act_health_body_inquiry.xml index 130946f..b1c0715 100644 --- a/health/src/main/res/layout/act_health_body_inquiry.xml +++ b/health/src/main/res/layout/act_health_body_inquiry.xml @@ -22,6 +22,7 @@ android:orientation="vertical"> () { } } - aMap?.animateCamera(CameraUpdateFactory.newLatLngBounds(boundsBuilder.build(),300)) + aMap?.animateCamera(CameraUpdateFactory.newLatLngBounds(boundsBuilder.build(),60)) } } diff --git a/network/src/main/java/com/xty/network/ApiInterface.kt b/network/src/main/java/com/xty/network/ApiInterface.kt index cf83e3b..bcb6420 100644 --- a/network/src/main/java/com/xty/network/ApiInterface.kt +++ b/network/src/main/java/com/xty/network/ApiInterface.kt @@ -1958,5 +1958,5 @@ interface ApiInterface { * 计算订单金额 */ @POST("customer/mall/order/computeAmount") - suspend fun computeAmount(@Body rb: RequestBody): RespBody + suspend fun computeAmount(@Body rb: RequestBody): RespBody } \ No newline at end of file diff --git a/network/src/main/java/com/xty/network/model/ComputeAmountBean.kt b/network/src/main/java/com/xty/network/model/ComputeAmountBean.kt new file mode 100644 index 0000000..36d5926 --- /dev/null +++ b/network/src/main/java/com/xty/network/model/ComputeAmountBean.kt @@ -0,0 +1,3 @@ +package com.xty.network.model + +data class ComputeAmountBean(var pay_price:String,var discount_price:Float) diff --git a/network/src/main/java/com/xty/network/model/FamilyHealthBean.kt b/network/src/main/java/com/xty/network/model/FamilyHealthBean.kt index c46ffaa..644d22c 100644 --- a/network/src/main/java/com/xty/network/model/FamilyHealthBean.kt +++ b/network/src/main/java/com/xty/network/model/FamilyHealthBean.kt @@ -9,6 +9,8 @@ data class FamilyHealthBean ( var isMy:Int, //是否本人0,否,1,是 var power:String?, //电量 var isRealTime:Int ,//是否实时,0,否,1,是 + var isBindWatch:Int,//0,未绑定,1,已绑定 + var dataList: ArrayList ,//数据列表 var avatarList:ArrayList? diff --git a/network/src/main/java/com/xty/network/model/InquiryInfoDetailBean.kt b/network/src/main/java/com/xty/network/model/InquiryInfoDetailBean.kt index ed7440e..903a7a8 100644 --- a/network/src/main/java/com/xty/network/model/InquiryInfoDetailBean.kt +++ b/network/src/main/java/com/xty/network/model/InquiryInfoDetailBean.kt @@ -5,19 +5,19 @@ data class InquiryInfoDetailBean( val view_num:Int, //浏览量 - val prevent:String, //预后 + var prevent:String?, //预后 - val condition:String, //状况 + var condition:String?, //状况 - val symptom:String,//症状 - val initial:String, //首字母 + var symptom:String?,//症状 + var initial:String, //首字母 - val daily:String, //日常 - val heal:String, //治疗 - val department:String, //问诊科室 + var daily:String?, //日常 + var heal:String?, //治疗 + var department:String?, //问诊科室 - val seek:String, //就医 + var seek:String?, //就医 - val etiology:String //病因 + var etiology:String? //病因 )