多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > 自定義View常用例子二(點擊展開隱藏控件,九宮格圖片控件)

自定義View常用例子二(點擊展開隱藏控件,九宮格圖片控件)

來源:程序員人生   發布時間:2016-07-04 16:42:41 閱讀次數:3062次

自定義View經常使用例子2(點擊展開隱藏控件,9宮格圖片控件)

今天博客的主要內容是兩個常見的自定義控件,第1個是我們常常看到的點擊隱藏點擊查看控件,第2個控件是仿微信朋友圈的9宮格圖片控件,相對上1篇的流布式布局來講,這篇博客更容易,只不過觸及更多的知識點而已

轉載請注明原博客地址:http://blog.csdn.net/gdutxiaoxu/article/details/51772308

1.空話不多說了,先來看1下效果圖

  1. 圖1效果,點擊隱藏,展開


  1. 圖2效果,類似于朋友圈9宮格圖片


圖2源碼下載地址

圖1源碼下載地址:

圖1源碼

```

public class CollapseView extends LinearLayout { private long duration = 350; private Context mContext; private TextView mNumberTextView; private TextView mTitleTextView; private RelativeLayout mContentRelativeLayout; private RelativeLayout mTitleRelativeLayout; private ImageView mArrowImageView; int parentWidthMeasureSpec; int parentHeightMeasureSpec; private String TAG = "xujun"; public CollapseView(Context context) { this(context, null); } public CollapseView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; LayoutInflater.from(mContext).inflate(R.layout.collapse_layout, this); initView(); } private void initView() { mNumberTextView = (TextView) findViewById(R.id.numberTextView); mTitleTextView = (TextView) findViewById(R.id.titleTextView); mTitleRelativeLayout = (RelativeLayout) findViewById(R.id.titleRelativeLayout); mContentRelativeLayout = (RelativeLayout) findViewById(R.id.contentRelativeLayout); mArrowImageView = (ImageView) findViewById(R.id.arrowImageView); mTitleRelativeLayout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { rotateArrow(); } }); mNumberTextView.setBackgroundResource(R.drawable.circle); Drawable circleShape = createCircleShape(Color.BLACK); mNumberTextView.setBackgroundDrawable(circleShape); collapse(mContentRelativeLayout); } public void setNumber(String number) { if (!TextUtils.isEmpty(number)) { mNumberTextView.setText(number); } } public void setTitle(String title) { if (!TextUtils.isEmpty(title)) { mTitleTextView.setText(title); } } public void setContent(int resID) { View view = LayoutInflater.from(mContext).inflate(resID, null); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, RelativeLayout .LayoutParams.WRAP_CONTENT); view.setLayoutParams(layoutParams); mContentRelativeLayout.addView(view); } /** * 若使用這個方法,強迫layoutParams must be RelativeLayout.LayoutParams,避免在某些情況下出現毛病 * * @param view */ public void setContent(View view) { ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); if (layoutParams == null) { layoutParams = new RelativeLayout.LayoutParams( LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT); } if (!(layoutParams instanceof RelativeLayout.LayoutParams)) { throw new IllegalStateException("layoutParams must be RelativeLayout.LayoutParams "); } view.setLayoutParams(layoutParams); mContentRelativeLayout.addView(view); } public void rotateArrow() { int degree = 0; if (mArrowImageView.getTag() == null || mArrowImageView.getTag().equals(true)) { mArrowImageView.setTag(false); degree = ⑴80; expand(mContentRelativeLayout); } else { degree = 0; mArrowImageView.setTag(true); collapse(mContentRelativeLayout); } mArrowImageView.animate().setDuration(duration).rotation(degree); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); parentWidthMeasureSpec = widthMeasureSpec; parentHeightMeasureSpec = heightMeasureSpec; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); } // 展開 private void expand(final View view) { view.setVisibility(View.VISIBLE); int childWidthMode = getMode(parentWidthMeasureSpec); int childHeightMode = getMode(parentHeightMeasureSpec); view.measure(childWidthMode, childHeightMode); final int measuredWidth = view.getMeasuredWidth(); final int measuredHeight = view.getMeasuredHeight(); final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1); valueAnimator.setDuration(duration); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float precent = animation.getAnimatedFraction(); int width = (int) (measuredWidth * precent); setWidth(view, width); if (precent == 1) { valueAnimator.removeAllUpdateListeners(); } } }); valueAnimator.start(); } private int getMode(int parentMeasureSpec) { if (parentMeasureSpec == MeasureSpec.EXACTLY) { return MeasureSpec.AT_MOST; } else if (parentMeasureSpec == MeasureSpec.AT_MOST) { return MeasureSpec.AT_MOST; } else { return parentMeasureSpec; } } private void setWidth(View view, int width) { ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); layoutParams.width = width; view.setLayoutParams(layoutParams); view.requestLayout(); } // 折疊 private void collapse(final View view) { final int measuredHeight = view.getMeasuredHeight(); final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1); valueAnimator.setDuration(duration); final int viewMeasuredWidth = view.getMeasuredWidth(); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float precent = animation.getAnimatedFraction(); Log.i(TAG, "onAnimationUpdate: precent" + precent); int width = (int) (viewMeasuredWidth - viewMeasuredWidth * precent); setWidth(view, width); // 動畫履行結束的時候,設置View為View.GONE,同時移除監聽器 if (precent == 1) { view.setVisibility(View.GONE); valueAnimator.removeAllUpdateListeners(); } } }); valueAnimator.start(); } }

```

思路解析

  1. 如圖所示,圖逐一4個部份組成,數字,標題,箭頭,圖片,點擊標題所在的那1行,圖片回相應地隱藏或顯示。
  2. 數字,標題,箭頭都在同1個相對布局里面,圖片在單獨的1個相對布局中,整體由 LinearLayout構成,布局文件以下

```

<?xml version="1.0" encoding="utf⑻"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ffffff" android:orientation="vertical"> <RelativeLayout android:id="@+id/titleRelativeLayout" android:padding="30px" android:layout_width="match_parent" android:layout_height="170px" android:clickable="true"> <TextView android:id="@+id/numberTextView" android:layout_width="70px" android:layout_height="70px" android:gravity="center" android:layout_centerVertical="true" android:clickable="false" android:text="1" android:textStyle="bold" android:textColor="#EBEFEC" android:textSize="35px" /> <TextView android:id="@+id/titleTextView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toRightOf="@id/numberTextView" android:layout_marginLeft="30px" android:clickable="false" android:textColor="#1d953f" android:textSize="46px" /> <ImageView android:id="@+id/arrowImageView" android:layout_width="48px" android:layout_height="27px" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:background="@mipmap/arrow_down" android:clickable="false" android:scaleType="fitCenter" /> </RelativeLayout> <View android:layout_width="match_parent" android:layout_height="2px" android:layout_below="@id/titleRelativeLayout" android:background="#E7E7EF" android:clickable="false" /> <RelativeLayout android:id="@+id/contentRelativeLayout" android:visibility="gone" android:layout_width="match_parent" android:layout_height="wrap_content"> </RelativeLayout> </LinearLayout>

```

把contentRelativeLayout的visibility設置為android:visibility="gone",是由于1開始不用加載這個相對局部,這樣它不會占用位置

3. 在代碼中初始化布局,并給 mTitleRelativeLayout設置點擊事件。


mTitleRelativeLayout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { rotateArrow(); } });

我們來看 rotateArrow()里面我們做了甚么,其實就是根據相應的動畫履行箭頭旋轉的動作和更改 contentRelativeLayout的高度 核心代碼以下:


int degree = 0; if (mArrowImageView.getTag() == null || mArrowImageView.getTag().equals(true)) { mArrowImageView.setTag(false); degree = ⑴80; expand(mContentRelativeLayout); } else { degree = 0; mArrowImageView.setTag(true); collapse(mContentRelativeLayout); } mArrowImageView.animate().setDuration(duration).rotation(degree);

我們在來看1下在expand我們做了甚么,其實就是給contentRelativeLayout履行1個動畫,在動畫的履行進程中不斷改變contentRelativeLayout的高度,注意在履行動畫之前,我們需要小調用view.measure(childWidthMode, childHeightMode);方法,這樣我們可能獲得到高度


view.setVisibility(View.VISIBLE); int childWidthMode = getMode(parentWidthMeasureSpec); int childHeightMode = getMode(parentHeightMeasureSpec); view.measure(childWidthMode, childHeightMode); final int measuredWidth = view.getMeasuredWidth(); final int measuredHeight = view.getMeasuredHeight(); final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1); valueAnimator.setDuration(duration); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float precent = animation.getAnimatedFraction(); int width = (int) (measuredWidth * precent); setWidth(view, width); // 動畫履行結束的時候,同時移除監聽器 if (precent == 1) { valueAnimator.removeAllUpdateListeners(); } } }); valueAnimator.start();

反之,隱藏也是履行1個動畫,不斷改變高度,只不太高度 是愈來愈小,直至為0為止

圖1的源碼分析到此為止,源碼下載地址:https://github.com/gdutxiaoxu/CustomViewDemo2.git


圖2源碼分析

源碼下載地址

1.思路解析,

1)首先我們自己自定義1個CustomImageView,在這個類里面我們給其提供了1個方法

public void setImageUrl(String url);

在這個方法里面其實我們做的工作就是Picasso框架加載圖片,即圖片交給CustomImageView自己去加載,更符合面向對象的4位

if (!TextUtils.isEmpty(url)) { this.url = url; if (isAttachedToWindow) { Picasso.with(getContext()).load(url).placeholder(new ColorDrawable(Color.parseColor("#f5f5f5"))).into(this); } }

2)接著我們自定義1個NineGridlayout,繼承ViewGroup,在這個類里面我們主要做的工作就是添加孩子,并肯定每一個孩子的位置

首先我們在構造方法里面初始化我們控件需要的寬度

public NineGridlayout(Context context, AttributeSet attrs) { super(context, attrs); ScreenTools screenTools=ScreenTools.instance(getContext()); // 初始總寬度 totalWidth=screenTools.getScreenWidth()-screenTools.dip2px(80); }

接著我們提供了setImagesData()方法,基本所有的邏輯都放在這里

  • 首先我們先根據孩子的數量肯定有多少行多少列
  • 接著判斷是不是需要復用 ,不需要復用的話,直接添加孩子,需要復用的話,判斷需要新設置的孩子的數量是不是大于緩存的孩子的數量,小于的話,繼續添加孩子,知道孩子的數量到達我們需要的孩子的數量為止,小于的話,移除過剩的緩存的孩子的數量
  • 接著再擺放每一個孩子的位置,并設置它的Url

注意我們這里之所以需要判斷是不是需要復用,是由于ListView或RecyclerView的緩存機制,若每次setImagesData()的時候直接添加,會致使添加多個孩子,若直接移除所有孩子的話,性能相對來講較差,所以我們進行緩存,到此源碼分析位置。

代碼以下

/** * 博客地址:http://blog.csdn.net/gdutxiaoxu * @author xujun * @time 2015/11/27 16:13. */ public class NineGridlayout extends ViewGroup { /** * 圖片之間的間隔 */ private int gap = 5; private int columns;//列數 private int rows;//行數 private List listData; private int totalWidth; private final int MAX_COLUMNS=3; private final int MAX_ROW3=3; public NineGridlayout(Context context) { this(context,null); } public NineGridlayout(Context context, AttributeSet attrs) { super(context, attrs); ScreenTools screenTools=ScreenTools.instance(getContext()); // 初始總寬度 totalWidth=screenTools.getScreenWidth()-screenTools.dip2px(80); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { } private void layoutChildrenView(){ int childrenCount = listData.size(); int singleWidth = (totalWidth - gap * (3 - 1)) / 3; int singleHeight = singleWidth; /** * 根據子view數量肯定高度,這里直接調用setLayoutParams設置NineGridlayout的高度 * */ LayoutParams params = getLayoutParams(); int marginHeight = getPaddingTop() + getPaddingTop(); params.height = singleHeight * rows + gap * (rows - 1)+marginHeight; setLayoutParams(params); //擺放孩子的位置 for (int i = 0; i < childrenCount; i++) { CustomImageView childrenView = (CustomImageView) getChildAt(i); childrenView.setImageUrl(((Image) listData.get(i)).getUrl()); int[] position = findPosition(i); // 加上getPaddingLeft(),為了支持Padding屬性 int left = (singleWidth + gap) * position[1]+getPaddingLeft(); int top = (singleHeight + gap) * position[0]+getPaddingTop(); int right = left + singleWidth; int bottom = top + singleHeight; childrenView.layout(left, top, right, bottom); } } private int[] findPosition(int childNum) { int[] position = new int[2]; for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { if ((i * columns + j) == childNum) { position[0] = i;//行 position[1] = j;//列 break; } } } return position; } public int getGap() { return gap; } public void setGap(int gap) { this.gap = gap; } public void setImagesData(List<Image> lists) { if (lists == null || lists.isEmpty()) { return; } //初始化布局 generateChildrenLayout(lists.size()); //這里做1個重用view的處理 if (listData == null) { int i = 0; while (i < lists.size()) { CustomImageView iv = generateImageView(); addView(iv,generateDefaultLayoutParams()); i++; } } else { int oldViewCount = listData.size(); int newViewCount = lists.size(); if (oldViewCount > newViewCount) { removeViews(newViewCount - 1, oldViewCount - newViewCount); } else if (oldViewCount < newViewCount) { for (int i = 0; i < newViewCount - oldViewCount; i++) { CustomImageView iv = generateImageView(); addView(iv,generateDefaultLayoutParams()); } } } listData = lists; layoutChildrenView(); } /** * 根據圖片個數肯定行列數量 * 對應關系以下 * num row column * 1 1 1 * 2 1 2 * 3 1 3 * 4 2 2 * 5 2 3 * 6 2 3 * 7 3 3 * 8 3 3 * 9 3 3 * * @param length */ private void generateChildrenLayout(int length) { if (length <= MAX_COLUMNS) { rows = 1; columns = length; } else if (length <= MAX_COLUMNS*2) { rows = 2; columns = MAX_COLUMNS; if (length == MAX_COLUMNS+1) { columns = 2; } } else { rows = 3; columns = MAX_COLUMNS; } } private CustomImageView generateImageView() { CustomImageView iv = new CustomImageView(getContext()); iv.setScaleType(ImageView.ScaleType.CENTER_CROP); iv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { } }); iv.setBackgroundColor(Color.parseColor("#f5f5f5")); return iv; } }

圖2源碼下載地址

圖1源碼下載地址:

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 日本免费精品 | 免费xxxxx在线观看网站 | 欧美日韩亚洲精品国产色 | 麻豆精品国产免费观看 | 成人一a毛片免费视频 | 一区二区三区国模大胆 | 亚洲日本视频在线观看 | 国产精品免费视频一区二区 | 成人免费a视频 | 久久精品国产亚洲麻豆 | 在线观看视频免费入口 | 亚洲精品国产第一区二区三区 | 看片福利 | 欧美精品国产一区二区三区 | 2022亚洲在线免费视频 | 免费在线看v片 | 色综合欧美综合天天综合 | 欧美日本亚洲 | 娇小老少配xxxxx性视频 | 亚洲乱码一区 | 伊人网络 | 欧美激情在线精品video | 自拍偷自拍亚洲精品情侣 | 亚州毛色毛片免费观看 | 欧美papa| 精品一区二区三区四区五区 | 高清视频在线观看 | 俺去久久| 波多野结衣国产精品 | 欧美一级aa天码毛片 | 欧美成人看片一区二区三区 | 午夜三级理论在线观看视频 | vvideos欧美极度另类 | 中文字幕不卡高清免费 | 一本久道热中字伊人 | 在线亚洲v日韩v | 亚洲午夜精品久久久久久成年 | 国产一区二区亚洲精品 | 天天综合在线观看 | 国产欧美综合在线一区二区三区 | 久久精品综合一区二区三区 |