用一种较为简单的方式实现画廊效果(RecyclerView)
首先先上效果图:
思路:
对于这一个效果,主要要的部分和普通的RecyclerView没有太大的差别主要是获取当前需要放大的那一个Item。然后要对所有当前页面上可见的Item进行高度的改变(这里为了偷懒只改变了高度,如果要同时改变宽度的话其实方法不会差太多,就是在计算的缩放比例的时候改一改就好了)。
之前在做的时候一开始参考了 >>>这一篇博客<<< ,至于这一篇博客是怎么操作的我不多加说明了大家可以点链接过去看看。但是在使用的时候我这边不知道是由于说明原因通过 onScrolled(RecyclerView recyclerView, int dx, int dy) 获取到的横向的累计距离会在快速滑动的时候出错,所以就放弃了,但是中间我任然有一些借鉴,所以推荐大家也可以看一下。
具体步骤:
- 新建一个Card类继承RecyclerView
- 新建一个FinalValue类用来存一些已经确定的值(比如Item的pading,width)
- 新建一个r_Item视图作为Item的视图(这部分代码就不贴的)。
- 新建一个CardAdapt类继承RecyclerView.Adapter
- 新建一个ScaleHelper类用在滑动时对RecyclerView的各个Item的操作
首先是Card类(RecyclerView)
这一个类没有什么好多说的就是继承RecyclerView就好了。
接下来是CardAdapt
对于CardAdapt的话,我们需要添加一个方法 onCreateViewHolder 用于初始化卡片Item的宽度(高度)。
其中 FinalValue.LeftCard 是我们要显示的那个Item的两边的Item的显示出来的宽度(我这里没有设置边距所以不用管)。
public void onCreateViewHolder(ViewGroup parent, View itemView) {
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) itemView.getLayoutParams();
lp.width = parent.getWidth() - Display.dip2px(itemView.getContext(), 2 * (FinalValue.LeftCard));
itemView.setLayoutParams(lp);
}
在这里用到了一个Display的类时用于不同单位之间的转换的类,下面给出代码。
public class Display {
public static int dip2px(Context context, float dpValue) {//将dip或dp转化为px
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
public static int px2dip(Context context, float pxValue) {//将px转化为dp或者是dip
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
}
同时我们还有添加几个方法:
1.设置每一个Item的宽
private void onCreateViewHolder(ViewGroup parent, View itemView) {//设置宽度
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) itemView.getLayoutParams();
lp.width = parent.getWidth() - DisplayUntil.dip2px(itemView.getContext(), 2 * (FinalValue.LeftCard));
itemView.setLayoutParams(lp);
}
2.为了可以使首尾两个Item也可以在屏幕中间显示
private void makecenter(View itemView, final int position, int itemCount) {//用于是首尾可以居中
int padding = DisplayUntil.dip2px(itemView.getContext(), FinalValue.padding);
itemView.setPadding(padding, 0, padding, 0);
int leftMar = position == 0 ? padding + DisplayUntil.dip2px(itemView.getContext(), FinalValue.LeftCard) : 0;
int rightMar = position == itemCount - 1 ? padding + DisplayUntil.dip2px(itemView.getContext(), FinalValue.LeftCard) : 0;
setMargin(itemView, leftMar, 0, rightMar, 0);
}
private void setMargin(View view, int left, int top, int right, int bottom) {
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
if (lp.leftMargin != left || lp.topMargin != top || lp.rightMargin != right || lp.bottomMargin != bottom) {
lp.setMargins(left, top, right, bottom);
view.setLayoutParams(lp);
}
}
其余的代码就不多说了
private ArrayList<String> mDatas = new ArrayList<>();
public cardAdapt (ArrayList<String> Data)
{
mDatas = Data;
}
@NonNull
@Override
public VH onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.r_item,viewGroup,false);
onCreateViewHolder(viewGroup,view);
return new VH(view);
}
@Override
public void onBindViewHolder(@NonNull VH vh, int i) {
makecenter(vh.itemView,i,getItemCount());
vh.mTextView.setText(mDatas.get(i));
}
@Override
public int getItemCount() {
return mDatas.size();
}
class VH extends RecyclerView.ViewHolder
{
TextView mTextView;
public VH(@NonNull View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.ttv);
}
}
然后就是最重要的ScaleHelper类
首先是要用到的变量
private cards mCard;
private Context mContext;
private int mCardWidth; // 卡片宽度
private int mAllWidth;//整个recyclerView的宽度
private int mCurrentPos;//要滚动到的页面
private LinearLayoutManager mLayoutManager;
private int mAllLeft;//主卡片的全部右边宽度
private int last = 0;
private LinearSnapHelper mLinearSnapHelper = new LinearSnapHelper();
我们需要在Card.addOnScrollListener中的 onScrolled 方法中添加获取当前的中心Item的逻辑。这里我们要用到的方式是mLayoutManager.findFirstCompletelyVisibleItemPosition() 这一个方式是获取当前完全可见的第一个Item的position,但是当当前页面没有可以完全显示的Item的时候就会需要特判。
出现这种情况有两种情况。
1.当当前页面时首尾页面的时候,即便会出现当前页面可见但是now为-1的情况,处理代码如下:
if(now == -1 && mCurrentPos == 0)//当当前页面时首尾页面的时候,即便会出现当前页面可见但是now为-1的情况所以下面做特判
{
View view = mLayoutManager.findViewByPosition(mCurrentPos);
if(view.getLeft() > 0)
{
onScrolledChangedCallback();
return ;
}
}
if(now == -1 && mCurrentPos == mLayoutManager.getItemCount()-1)
{
View view = mLayoutManager.findViewByPosition(mCurrentPos);
if(view.getRight() > 0)
{
onScrolledChangedCallback();
return ;
}
}
2.真的只是没有页面完全可见,处理代码如下:
if(now!=-1)
{
mCurrentPos = now;
}else if(last * dx <0)
{
mCurrentPos += dx>0?1:-1;
}
last = dx;
然后在处理完之后调用对应的接下来会讲到的调整宽度的方法就可以了。
对于处理宽度的方法的话,其实就是根据获取到的当前的页面的position来获取主Item和两边的Item(如果有的话),再通过自己写的一个获取对应百分比的方法 getlper() (接下来会讲)来改变页面的高度,代码如下:
public void onScrolledChangedCallback() {
View leftView = null;
View currentView;
View rightView = null;
//页面赋值
if (mCurrentPos > 0) {
leftView = mCard.getLayoutManager().findViewByPosition(mCurrentPos - 1);
}
currentView = mCard.getLayoutManager().findViewByPosition(mCurrentPos);
if (mCurrentPos < mCard.getAdapter().getItemCount() - 1) {
rightView = mCard.getLayoutManager().findViewByPosition(mCurrentPos + 1);
}
//调整大小
if (leftView != null) {
int left = leftView.getLeft();
float percent = getlper(left);
leftView.setScaleY((float) (0.8 + 0.2 * percent));
}
if (currentView != null) {
int left = currentView.getLeft();
float percent = getlper(left);
currentView.setScaleY((float) (0.8 + 0.2 * percent));
}
if (rightView != null) {
int left = rightView.getLeft();
float percent = getlper(left);
rightView.setScaleY((float) (0.8 + 0.2 * percent));
}
}
接下来就是之前提到的获取当前页面应当显示的高度比例的方法,代码如下:
private float getlper(int left)
{
if(left >= mAllWidth - DisplayUntil.dip2px( mContext,FinalValue.LeftCard) || left <= -(mCardWidth - DisplayUntil.dip2px( mContext,FinalValue.LeftCard)))
{
return 0;
}else if(left > mAllLeft )
{
left = mAllWidth - DisplayUntil.dip2px( mContext,FinalValue.LeftCard) - left;
return (float)(left * 1.0 / (mAllWidth - DisplayUntil.dip2px( mContext,FinalValue.LeftCard) - mAllLeft));
}else
{
left += (mCardWidth - DisplayUntil.dip2px( mContext,FinalValue.LeftCard));
return (float)(left * 1.0 / (mCardWidth - DisplayUntil.dip2px( mContext,FinalValue.LeftCard) + mAllLeft ));
}
}
还有不要忘记要添加一个方法,并调用:
private void initWidth() {
mCard.post(new Runnable() {
@Override
public void run() {
Log.i("show", "run: ");
mAllWidth = mLayoutManager.getWidth();
mCardWidth = mAllWidth - DisplayUntil.dip2px(mContext, 2 * (FinalValue.LeftCard));
mAllLeft = DisplayUntil.dip2px(mContext, (FinalValue.LeftCard));
mCard.smoothScrollToPosition(mCurrentPos);//让RecycleView平滑滚动到指定位置(mCurrentItemPos) 同时要重写smoothScrollToPosition(RecyclerView, State, int)方法
onScrolledChangedCallback();
}
});
}
同时要实现一个方法用于关联对应的Card:
public void attachToRecyclerView(final cards mCard , LinearLayoutManager ml) {
this.mCard = mCard;
mContext = mCard.getContext();
mLayoutManager = ml;
addOnScrollListener();
initWidth();
mLinearSnapHelper.attachToRecyclerView(mCard);
}
趁年轻,别给自己后悔的机会,错过了就什么都来不及了。