編輯:關於Android編程
RecyclerView 已經出來很久了,但是在項目中之前都使用的是ListView,最近新的項目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,網上吧啦吧啦沒有合適的自己總結了一哈。
先貼圖上來看看:
vcq9o7o8YnIgLz4NCjEuyrnTw8+1zbPX1LT4tcRBbmRyb2lkLnN1cHBvcnQudjQud2lkZ2V0LlN3aXBlUmVmcmVzaExheW91dNXiuPa/2LzbwLTKtc/WoaM8YnIgLz4NCjIu19S2qNLltcTA78PmtPjT0FJlY3lsZVZpZXe1xL/YvP6hozxiciAvPg0KyrnTw1JlY3ljbGVWaWV3utyyu7rDzO280823sr+jrNaux7DU2sq508NsaXN0dmlld7Wx1tDX1Ly6v8nS1MztvNNoZWFkZXK6zWJvb3Rto6y1q8rHUmVjeWNsZVZpZXe6w8/xsrvKx8THw7S1xLrDstnX96GjttTT2rXa0rvW1sq508PPtc2z19S0+LXEQW5kcm9pZC5zdXBwb3J0LnY0LndpZGdldC5Td2lwZVJlZnJlc2hMYXlvdXTAtMq1z9a1xKOs0rK63LrD08OjrLWrysey+sa30ruw47K70qrV4tbWz8LArcui0MKjrM6qwcvIw9fUvLrP1LXDxaOxxqOsy/vSu7Dju+G449K7uPbX1Ly6tcS0+NPQtq+7raOs1eK+zbHIvc+ztrWtwcuho6GjoaPL+dLUvs3Wu8Tc08O3vbeoMsHLoaM8YnIgLz4NCrTz1sLLtdK7uf63vbeoMrXEyrXP1re9yr2jrLi4sry+1s6qVmlld0dyb3VwLMDvw+bM7bzTVmlld7Xa0ru49s6qv9i8/s6qaGVhZGVytdq2/rj2v9i8/s6qUmVjeWNsZVZpZXejrNbB09rX7rXXsr+1xM/CwK2809TYuPy24MrUzbyjrM2ouf1SZWN5Y2xlVml3tcRBZGFwdGVywLTM7bzToaM8YnIgLz4NCtPQwcvLvMK3vs2448bwwLSjujwvcD4NCjxwcmUgY2xhc3M9"brush:java;">
package com.krain.srecyclerview.fruitview;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.krain.srecyclerview.R;
/**
* Created by dafuShao on 2016/9/9 0009.
*
*/
public class ElizabethView extends FrameLayout {
private ImageView imageView;
private AnimationDrawable animationDrawable;
public ElizabethView(Context context) {
super(context);
initview(context);
}
public ElizabethView(Context context, AttributeSet attrs) {
super(context, attrs);
initview(context);
}
public ElizabethView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initview(context);
}
private void initview(Context context){
View view= LayoutInflater.from(context).inflate(R.layout.elizabeth_item,null);
imageView=(ImageView) view.findViewById(R.id.elizabeth_im);
animationDrawable= (AnimationDrawable) imageView.getBackground();
addView(view);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
//開始動畫
public void startAnim(){
animationDrawable.start();
}
//停止動畫
public void stopAnim(){
animationDrawable.stop();
}
}
這是頭部控價很簡單裡面有一個小的臭蛋眼睛左右擠一哈的效果就不貼圖了。
下面是自定義的包含RecyclerView的控件代碼如下:
package com.krain.srecyclerview.srecyclerview; import android.content.Context; import android.os.Handler; import android.os.Message; import android.support.v4.view.MotionEventCompat; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.Scroller; import android.widget.TextView; import com.krain.srecyclerview.R; import com.krain.srecyclerview.fruitview.ElizabethView; public class SRecyclerView extends ViewGroup { Context context; RecyclerView mRecyclerView; ElizabethView mHeaderView; TextView mFootViewTips;//footview的文字顯示 AdapterWrapper mAdapter; boolean mIsTop = true;//是否滑動到了最頂部 RecyclerView.LayoutManager mLayoutManager; int mLastVisibleItem; int mFirstVisibleItem; OnRecyclerStatusChangeListener mRecyclerChangeListener; int mStatus;//當前狀態 int mHeadviewHeight;//headview的高度 Scroller mScroller; int mFristScollerY;//最開始的getscrolly boolean mHasFooter;//是否有上拉加載的功能 boolean mShowFootVisible;//是否顯示FOOTERview在mHasFooter為true的時候生效 boolean mHasRefresh = true;//是否支持下拉刷新 private final int DEFAULT_MIN_PAGEINDEX = 1;//默認最小的頁數 int mMaxPage = DEFAULT_MIN_PAGEINDEX;//分頁的總頁數 int mCurrentPage = DEFAULT_MIN_PAGEINDEX;//當前的頁數,從1開始 private final int STATUS_NORMAL = 0, STATUS_REFRESH = 1, STATUS_LOAD = 2; private final int MSG_LOAD_COMPLETE = 1, MSG_REFRESH_COMPLETE = 0;//handle的常量 private final int DELAY_LOAD_COMPLETE = 1000, DELAY_REFRESH_COMPLETE = 1000;//加載完成延時回收的時間 public SRecyclerView(Context context) { super(context); init(context); } public SRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public SRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } /** * 設置最大頁數 * * @param maxPage */ public void setMaxPage(int maxPage) { this.mMaxPage = maxPage; } /** * 是否支持上拉加載 * * @param hasLoadmore */ public void setLoadmore(boolean hasLoadmore) { mHasFooter = hasLoadmore; } public void setRecyclerViewLayoutManage(RecyclerView.LayoutManager mLayoutManage){ this.mLayoutManager=mLayoutManage; } /** * 關閉下拉刷新功能 */ public void disableRefresh() { mHasRefresh = false; } public void setAdapter(BaseRecyclerViewAdapter adapter) { int height = 0; if (mMaxPage == DEFAULT_MIN_PAGEINDEX) { mHasFooter = false; } mAdapter = new AdapterWrapper(context, adapter); mRecyclerView.setAdapter(mAdapter); } private int getViewHeight(View view) { int measure = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); view.measure(measure, measure); return view.getMeasuredHeight(); } /** * 獲取viewgroup裡面的recyclerview * * @return */ public RecyclerView getRecyclerView() { return mRecyclerView; } public void setOnRecyclerChangeListener(OnRecyclerStatusChangeListener listener) { mRecyclerChangeListener = listener; } /** * 設置RecyclerView添加、刪除Item的動畫 * * @param animator */ public void setItemAnimator(RecyclerView.ItemAnimator animator) { mRecyclerView.setItemAnimator(animator); } public void notifyDataSetChanged() { mStatus = STATUS_NORMAL;//重新set數據代表loadmore結束了,此時恢復成普通狀態 mAdapter.notifyDataSetChanged(); } public void notifyDataInsert(int positionStart, int itemCount) { mStatus = STATUS_NORMAL;//重新set數據代表loadmore結束了,此時恢復成普通狀態 mAdapter.notifyItemRangeInserted(positionStart, itemCount); } public void notifyDataRemove(int position) { mStatus = STATUS_NORMAL;//重新set數據代表loadmore結束了,此時恢復成普通狀態 mAdapter.notifyItemRemoved(position); } /** * 初始化操作 * * @param context */ void init(Context context) { this.context = context; mScroller = new Scroller(context); // if (mLayoutManager!=null){ // addChildView(context,mLayoutManager); // }else{ // addChildView(context, new LinearLayoutManager(context)); // } addChildView(context); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } } /** * 增加子View * * @param context */ void addChildView(Context context, RecyclerView.LayoutManager mLayoutManager) { ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mHeaderView = new ElizabethView(context); mRecyclerView = new RecyclerView(context); addView(mHeaderView); addView(mRecyclerView); //mLayoutManager = new LinearLayoutManager(context); // mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(mLayoutManager); // 設置Item增加、移除默認動畫 mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mRecyclerView.addOnScrollListener(onScrollListener); mRecyclerView.setHasFixedSize(true); mRecyclerView.setLayoutParams(params); } void addChildView(Context contex) { ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mHeaderView = new ElizabethView(context); mRecyclerView = new RecyclerView(context); addView(mHeaderView); addView(mRecyclerView); //mLayoutManager = new LinearLayoutManager(context); mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(mLayoutManager); // 設置Item增加、移除默認動畫 mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mRecyclerView.addOnScrollListener(onScrollListener); mRecyclerView.setHasFixedSize(true); mRecyclerView.setLayoutParams(params); } /** * 屏蔽Recyclerview的觸摸事件(下拉刷新的時候) */ float lastY; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); switch (action) { case MotionEvent.ACTION_DOWN: lastY = ev.getRawY(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: return false; case MotionEvent.ACTION_MOVE: if (mHasRefresh && mIsTop && ev.getRawY() > lastY) return true; break; } return false; } float offsetY = 0; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: offsetY = Math.abs(event.getRawY() - lastY); //Y差值絕對值 if (offsetY > 0) scrollToOffset(offsetY); else { mIsTop = false; } break; case MotionEvent.ACTION_UP: if (getScrollY() <= 0) { doRefresh(); mHeaderView.stopAnim(); mHeaderView.startAnim(); } else complete(); break; } return super.onTouchEvent(event); } /** * 滾動這個view到手滑動的位置 * * @param offsetY Y軸偏移量 */ void scrollToOffset(float offsetY) { //假如正在刷新並且現在的scrolly和初始值一樣的時候,代表准備下拉開始刷新,並執行一次only if (getScrollY() == mFristScollerY && mRecyclerChangeListener != null) mRecyclerChangeListener.startRefresh(); int value = Math.round(offsetY / 2.0F); value = mFristScollerY - value; scrollTo(0, value); } /** * 執行刷新操作,移動到header剛出來的位置 */ void doRefresh() { mStatus = STATUS_REFRESH; int currentY = getScrollY(); mScroller.startScroll(0, currentY, 0, (mFristScollerY - mHeadviewHeight) - currentY); invalidate(); if (mRecyclerChangeListener != null) mRecyclerChangeListener.onRefresh(); handler.sendEmptyMessageDelayed(MSG_REFRESH_COMPLETE, DELAY_REFRESH_COMPLETE); } Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == MSG_LOAD_COMPLETE) { View footview = mAdapter.getFootView(); if (footview != null) mRecyclerView.smoothScrollBy(0, -footview.getMeasuredHeight()); } else if (msg.what == MSG_REFRESH_COMPLETE) complete(); } }; /** * header返回原處完全隱藏 */ public void complete() { mCurrentPage = DEFAULT_MIN_PAGEINDEX;//完成之後當前的page恢復默認值 if (mFootViewTips != null) mFootViewTips.setText(context.getString(R.string.loading));//更改foot提示為正在加載中 if (mRecyclerChangeListener != null) mRecyclerChangeListener.refreshComplete(); mStatus = STATUS_NORMAL; int currentY = getScrollY(); mScroller.startScroll(0, currentY, 0, mFristScollerY - currentY); mHeaderView.stopAnim(); invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); measureChild(child, widthMeasureSpec, heightMeasureSpec); height += child.getMeasuredHeight(); } setMeasuredDimension(width, height); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int left = getPaddingLeft(); int top = getPaddingTop(); for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (i == 0) {//當是header的時候居中顯示 int headerLeft = getMeasuredWidth() / 2 - child.getMeasuredWidth() / 2; child.layout(headerLeft, top, headerLeft + child.getMeasuredWidth(), top + child.getMeasuredHeight()); } else child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()); top += child.getMeasuredHeight(); } mHeadviewHeight = getPaddingTop() + mHeaderView.getMeasuredHeight(); scrollTo(0, mHeadviewHeight);//移動到header下方以顯示recyleview mFristScollerY = getScrollY(); } /** * RecyclerView的滑動監聽事件 */ RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_IDLE) { //滑動到了頂部 if (mFirstVisibleItem == 0) { mIsTop = true; } else { mIsTop = false; if (mStatus != STATUS_LOAD && mShowFootVisible && mLastVisibleItem + 1 == mAdapter.getItemCount()) { if (mCurrentPage == mMaxPage) { //當前頁面是最後一頁的時候 mFootViewTips = (TextView) mAdapter.getFootView().findViewById(R.id.footer_tips); mFootViewTips.setText(context.getString(R.string.last_page_tips)); handler.sendEmptyMessageDelayed(MSG_LOAD_COMPLETE, DELAY_LOAD_COMPLETE); } else { mStatus = STATUS_LOAD; if (mRecyclerChangeListener != null) { mRecyclerChangeListener.onLoadMore(); mCurrentPage++; } } } } } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); if (mLayoutManager instanceof LinearLayoutManager) { mLastVisibleItem = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition(); mFirstVisibleItem = ((LinearLayoutManager) mLayoutManager).findFirstCompletelyVisibleItemPosition(); setFootviewVisible(); } else if (mLayoutManager instanceof GridLayoutManager) { mLastVisibleItem = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition(); mFirstVisibleItem = ((GridLayoutManager) mLayoutManager).findFirstCompletelyVisibleItemPosition(); setFootviewVisible(); } else if (mLayoutManager instanceof StaggeredGridLayoutManager) { //因為StaggeredGridLayoutManager的特殊性可能導致最後顯示的item存在多個,所以這裡取到的是一個數組 //得到這個數組後再取到數組中position值最大的那個就是最後顯示的position值了 int[] lastPositions = new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()]; ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(lastPositions); mLastVisibleItem = findMax(lastPositions); mFirstVisibleItem = ((StaggeredGridLayoutManager) mLayoutManager).findFirstVisibleItemPositions(lastPositions)[0]; setFootviewVisible(); } } }; void setFootviewVisible() { //當設置了擁有上拉加載功能但是第一頁的條目不足以盛滿Recyclerview的時候隱藏footer if (mHasFooter && mFirstVisibleItem == 0) { /** * 這裡加上一個mShowFootVisible在上拉加載功能啟用的情況下生效,從來控制item數量不足鋪滿 * recyclerview高度的時刻隱藏footview,在item數量超過view高度的情況下顯示 */ if (mLastVisibleItem + 1 == mAdapter.getItemCount()) { mShowFootVisible = false; } else mShowFootVisible = true; notifyDataSetChanged(); } } private int findMax(int[] positions) { int max = positions[0]; for (int value : positions) { if (value > max) { max = value; } } return max; } private class AdapterWrapper extends RecyclerView.Adapter { private static final int TYPE_ITEM = 0; private static final int TYPE_FOOTER = 1; private RecyclerView.Adapter mAdapter; private Context mContext; View footer; public AdapterWrapper(Context context, RecyclerView.Adapter wrappedAdapter) { this.mContext = context; this.mAdapter = wrappedAdapter; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { RecyclerView.ViewHolder holder = null; switch (viewType) { case TYPE_ITEM: holder = mAdapter.onCreateViewHolder(parent, viewType); break; case TYPE_FOOTER: footer = LayoutInflater.from(mContext).inflate(R.layout.lib_recyle_footview, null); LinearLayout linearLayout= (LinearLayout) footer.findViewById(R.id.loading_layout); StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); layoutParams.setFullSpan(true); linearLayout.setLayoutParams(layoutParams); holder = new FooterViewHolder(footer); break; } return holder; } public View getFootView() { return footer; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (!mHasFooter || position + 1 != getItemCount()) { mAdapter.onBindViewHolder(holder, position); } } @Override public int getItemCount() { return mShowFootVisible ? mAdapter.getItemCount() + 1 : mAdapter.getItemCount(); } @Override public int getItemViewType(int position) { if (mShowFootVisible && position + 1 == getItemCount()) { return TYPE_FOOTER; } else { return TYPE_ITEM; } } private class FooterViewHolder extends RecyclerView.ViewHolder { public ProgressBar progressBar; public TextView tvLoading; public LinearLayout llyLoading; public FooterViewHolder(View itemView) { super(itemView); progressBar = (ProgressBar) itemView.findViewById(R.id.progress_loading); tvLoading = (TextView) itemView.findViewById(R.id.footer_tips); llyLoading = (LinearLayout) itemView.findViewById(R.id.loading_layout); } } } }
最後還有一個就是Adapter的代碼:
package com.krain.srecyclerview.srecyclerview; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; public abstract class BaseRecyclerViewAdapterextends RecyclerView.Adapter { private OnItemClickLisener mItemListener; @Override public VH onCreateViewHolder(ViewGroup parent, int viewType) { View view = getItemView(viewType, parent); VH vh = getViewHolder(view); view.setOnClickListener(new OnRecyclerAdapterclickListener(vh, viewType)); view.setOnLongClickListener(new OnRecyclerAdapterclickListener(vh, viewType)); return vh; } public abstract VH getViewHolder(View itemView); /** * 返回item的view * * @return */ public abstract View getItemView(int viewType, ViewGroup parent); /** * 返回Adapter每個itemn的數據 可選 */ public Object getItem(int position) { return null; } /** * item點擊事件接口 * * @param mItemListener */ public void setOnItemListener(OnItemClickLisener mItemListener) { this.mItemListener = mItemListener; } @Override public abstract void onBindViewHolder(VH holder, int position); @Override public abstract int getItemCount(); class OnRecyclerAdapterclickListener implements View.OnClickListener, View.OnLongClickListener { VH viewholder; int viewType; public OnRecyclerAdapterclickListener(VH viewholder, int viewType) { this.viewholder = viewholder; this.viewType = viewType; } @Override public void onClick(View v) { if (mItemListener != null && viewholder.getAdapterPosition() != RecyclerView.NO_POSITION) { mItemListener.onItemClick(viewholder.getAdapterPosition(), viewType, viewholder, v); } } @Override public boolean onLongClick(View v) { if (mItemListener != null && viewholder.getAdapterPosition() != RecyclerView.NO_POSITION) { mItemListener.onItemLongClick(viewholder.getAdapterPosition(), viewType, viewholder, v); } return false; } } }
需要注意一哈:
如果想改變Reciview的布局方式在這個修改
void addChildView(Context contex) { ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mHeaderView = new ElizabethView(context); mRecyclerView = new RecyclerView(context); addView(mHeaderView); addView(mRecyclerView); //mLayoutManager = new LinearLayoutManager(context); mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(mLayoutManager); // 設置Item增加、移除默認動畫 mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mRecyclerView.addOnScrollListener(onScrollListener); mRecyclerView.setHasFixedSize(true); mRecyclerView.setLayoutParams(params); }
mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);這一行就是了改成對應的就可以了,這兒沒有封裝過幾天封裝一個出來。 還需要注意一哈就是在滑動判斷是不是最後一行,如果是瀑布流就要注意哈判斷的方式和其他的不一樣,
if (mLayoutManager instanceof StaggeredGridLayoutManager) { //因為StaggeredGridLayoutManager的特殊性可能導致最後顯示的item存在多個,所以這裡取到的是一個數組 //得到這個數組後再取到數組中position值最大的那個就是最後顯示的position值了 int[] lastPositions = new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()]; ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(lastPositions); mLastVisibleItem = findMax(lastPositions); mFirstVisibleItem = ((StaggeredGridLayoutManager) mLayoutManager).findFirstVisibleItemPositions(lastPositions)[0]; setFootviewVisible(); }
因為StaggeredGridLayoutManager的特殊性可能導致最後顯示的item存在多個,所以這裡取到的是一個數組,得到這個數組後再取到數組中position值最大的那個就是最後顯示的position值了
再去設置最後一行加載更多的顯示。
代碼稍後放出來,我再精簡一哈。
概述:如果不是一些特殊的情況,我想大家很少會接觸到這個需求。其實Android的Java部分沒有提供相應的接口,這裡需要去調用C的代碼,也就是說要寫JNI了。關於JNI的
第3節 TextView這是界面設計最為常用的控件,也是很多別的控件的父類,例如Button。3.1 文字常用屬性最常使用到的屬性,通過它們的名字就可以判斷出它們的作用:
1、前言Android Studio是基於IntelliJ IDEA下官方整和的一個Android應用程序開發環境。在IntelliJ強大的代碼編輯器和開發工具基礎之上,
先看看效果圖:實現思路:擦除圖片相應的角,然後層疊圖片,產生傾斜效果代碼實現:1、定義屬性在values文件夾下的attrs文件添加以下代碼<resources&g