Android 自訂ViewGroup手把手教你實現ArcMenu,androidviewgroup

來源:互聯網
上載者:User

Android 自訂ViewGroup手把手教你實現ArcMenu,androidviewgroup

轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/37567907

逛eoe發現這樣的UI效果,感覺很不錯,後來知道github上有這麼個開源項目~~~~當然本篇不是教你如何使用這個開源項目,而是教你如何自己通過自訂ViewGroup寫這樣的效果,自訂ViewGroup也是我的痛楚,嘿嘿,希望以此可以拋磚引玉~~

:



1、實現思路

通過,會有幾個問題:

a、動畫效果如何?

可以看齣動畫是從頂點外外發射的,可能有人說,那還不簡單,預設元素都在定點位置,然後TraslateAnimation就好了;這樣忽略了一點,就是TraslateAnimation雖然有動畫效果,但是本質是不會改變按鈕的位置,我們的按鈕動畫結束是要點擊的;有人可能會說那使用屬性動畫,或者改變leftMagin,rightMagin;這樣可能比較麻煩,其實我們可以預設讓子功能表就已經在目標位置,然後GONE,當點擊時還是用TraslateAnimation,把起始位置設為定點,終點位置就是我們隱藏的地區,動畫結束VISIBLE.

b、如何確定位置呢?

這可能需要一點數學上的知識,我畫了一張草圖(冰天雪地,跪玻璃碴子求畫下面這些圖的工具):


每次會根據子功能表數量,算出a這個角度,然後通過sin , cos 分別算出每個子功能表的left , top ;

當然這是在左上的情況,如果在右上,則top還是和左上一致的,left則為 (螢幕寬度-左上算出的left) ;其他兩個方位同理~


整體我通過自訂一個ViewGroup,這個ViewGroup中第一個子項目為點擊的按鈕(你可以隨便布局,隨便用什麼控制項),接下來的子項目我認為是功能表項目。根據,決定展開半徑和顯示的位置,讓使用者自己去定製。下面看具體實現:

2、自訂View的屬性:

<?xml version="1.0" encoding="utf-8"?><resources>    <attr name="position">        <enum name="left_top" value="0" />        <enum name="right_top" value="1" />        <enum name="right_bottom" value="2" />        <enum name="left_bottom" value="3" />    </attr>    <attr name="radius" format="dimension"></attr>    <declare-styleable name="ArcMenu">        <attr name="position" />        <attr name="radius"/>    </declare-styleable></resources>

3、在自訂的ViewGroup中擷取這些屬性

Arcmenu.java

/** * @author zhy */public class ArcMenu extends ViewGroup implements OnClickListener{private static final String TAG = "ArcMenu";/** * 菜單的顯示位置 */private Position mPosition = Position.LEFT_TOP;/** * 菜單顯示的半徑,預設100dp */private int mRadius = 100;/** * 使用者點擊的按鈕 */private View mButton;/** * 當前ArcMenu的狀態 */private Status mCurrentStatus = Status.CLOSE;/** * 回調介面 */private OnMenuItemClickListener onMenuItemClickListener;/** * 狀態的枚舉類 *  * @author zhy *  */public enum Status{OPEN, CLOSE}/** * 設定菜單現實的位置,四選1,預設右下 *  * @author zhy */public enum Position{LEFT_TOP, RIGHT_TOP, RIGHT_BOTTOM, LEFT_BOTTOM;}public interface OnMenuItemClickListener{void onClick(View view, int pos);}public ArcMenu(Context context){this(context, null);}public ArcMenu(Context context, AttributeSet attrs){this(context, attrs, 0);}/** * 初始化屬性 *  * @param context * @param attrs * @param defStyle */public ArcMenu(Context context, AttributeSet attrs, int defStyle){super(context, attrs, defStyle);// dp convert to pxmRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,mRadius, getResources().getDisplayMetrics());TypedArray a = context.getTheme().obtainStyledAttributes(attrs,R.styleable.ArcMenu, defStyle, 0);int n = a.getIndexCount();for (int i = 0; i < n; i++){int attr = a.getIndex(i);switch (attr){case R.styleable.ArcMenu_position:int val = a.getInt(attr, 0);switch (val){case 0:mPosition = Position.LEFT_TOP;break;case 1:mPosition = Position.RIGHT_TOP;break;case 2:mPosition = Position.RIGHT_BOTTOM;break;case 3:mPosition = Position.LEFT_BOTTOM;break;}break;case R.styleable.ArcMenu_radius:// dp convert to pxmRadius = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100f,getResources().getDisplayMetrics()));break;}}a.recycle();}

4、計運算元元素的大小:

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){int count = getChildCount();for (int i = 0; i < count; i++){// mesure childgetChildAt(i).measure(MeasureSpec.UNSPECIFIED,MeasureSpec.UNSPECIFIED);}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}

5、確定子項目的位置:

@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b){if (changed){layoutButton();int count = getChildCount();/** * 設定所有孩子的位置 例如(第一個為按鈕): 左上時,從左至右 ] 第2個:mRadius(sin0 , cos0) * 第3個:mRadius(sina ,cosa) 註:[a = Math.PI / 2 * (cCount - 1)] * 第4個:mRadius(sin2a ,cos2a) 第5個:mRadius(sin3a , cos3a) ... */for (int i = 0; i < count - 1; i++){View child = getChildAt(i + 1);child.setVisibility(View.GONE);int cl = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2)* i));int ct = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2)* i));// childview widthint cWidth = child.getMeasuredWidth();// childview heightint cHeight = child.getMeasuredHeight();// 右上,右下if (mPosition == Position.LEFT_BOTTOM|| mPosition == Position.RIGHT_BOTTOM){ct = getMeasuredHeight() - cHeight - ct;}// 右上,右下if (mPosition == Position.RIGHT_TOP|| mPosition == Position.RIGHT_BOTTOM){cl = getMeasuredWidth() - cWidth - cl;}Log.e(TAG, cl + " , " + ct);child.layout(cl, ct, cl + cWidth, ct + cHeight);}}}

首先在layoutButton中對按鈕位置就行設定,以及初始化點擊事件;然後從第二個子項目開始為功能表項目,分別設定其位置,計算的原理就是上面我畫的草圖,可以再去仔細看看,動手在紙上畫一畫。

/** * 第一個子項目為按鈕,為按鈕布局且初始化點擊事件 */private void layoutButton(){View cButton = getChildAt(0);cButton.setOnClickListener(this);int l = 0;int t = 0;int width = cButton.getMeasuredWidth();int height = cButton.getMeasuredHeight();switch (mPosition){case LEFT_TOP:l = 0;t = 0;break;case LEFT_BOTTOM:l = 0;t = getMeasuredHeight() - height;break;case RIGHT_TOP:l = getMeasuredWidth() - width;t = 0;break;case RIGHT_BOTTOM:l = getMeasuredWidth() - width;t = getMeasuredHeight() - height;break;}Log.e(TAG, l + " , " + t + " , " + (l + width) + " , " + (t + height));cButton.layout(l, t, l + width, t + height);}

這是定位Button的代碼,此時的代碼已經實現了定位,如果你把onLayout中childView.setVisibility(VISIBLE)。ArcMenu的整個控制項的樣子已經實現了,接下來就是點擊事件,已經效果動畫的實現了。

6、設定按鈕點擊事件

/** * 為按鈕添加點擊事件 */@Overridepublic void onClick(View v){mButton = findViewById(R.id.id_button);if (mButton == null){mButton = getChildAt(0);}rotateView(mButton, 0f, 270f, 300);toggleMenu(300);}

/** * 按鈕的旋轉動畫 *  * @param view * @param fromDegrees * @param toDegrees * @param durationMillis */public static void rotateView(View view, float fromDegrees,float toDegrees, int durationMillis){RotateAnimation rotate = new RotateAnimation(fromDegrees, toDegrees,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);rotate.setDuration(durationMillis);rotate.setFillAfter(true);view.startAnimation(rotate);}public void toggleMenu(int durationMillis){int count = getChildCount();for (int i = 0; i < count - 1; i++){final View childView = getChildAt(i + 1);childView.setVisibility(View.VISIBLE);int xflag = 1;int yflag = 1;if (mPosition == Position.LEFT_TOP|| mPosition == Position.LEFT_BOTTOM)xflag = -1;if (mPosition == Position.LEFT_TOP|| mPosition == Position.RIGHT_TOP)yflag = -1;// child leftint cl = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2) * i));// child topint ct = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2) * i));AnimationSet animset = new AnimationSet(true);Animation animation = null;if (mCurrentStatus == Status.CLOSE){// to openanimset.setInterpolator(new OvershootInterpolator(2F));animation = new TranslateAnimation(xflag * cl, 0, yflag * ct, 0);childView.setClickable(true);childView.setFocusable(true);} else{// to closeanimation = new TranslateAnimation(0f, xflag * cl, 0f, yflag* ct);childView.setClickable(false);childView.setFocusable(false);}animation.setAnimationListener(new AnimationListener(){public void onAnimationStart(Animation animation){}public void onAnimationRepeat(Animation animation){}public void onAnimationEnd(Animation animation){if (mCurrentStatus == Status.CLOSE)childView.setVisibility(View.GONE);}});animation.setFillAfter(true);animation.setDuration(durationMillis);// 為動畫設定一個開始延遲時間,純屬好看,可以不設animation.setStartOffset((i * 100) / (count - 1));RotateAnimation rotate = new RotateAnimation(0, 720,Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);rotate.setDuration(durationMillis);rotate.setFillAfter(true);animset.addAnimation(rotate);animset.addAnimation(animation);childView.startAnimation(animset);final int index = i + 1;childView.setOnClickListener(new View.OnClickListener(){@Overridepublic void onClick(View v){if (onMenuItemClickListener != null)onMenuItemClickListener.onClick(childView, index - 1);menuItemAnin(index - 1);changeStatus();}});}changeStatus();Log.e(TAG, mCurrentStatus.name() +"");}

點擊時,觸發TanslateAnimation動畫,從定點向外擴充,也給點擊按鈕添加了一個旋轉動畫,每個子功能表項同樣添加了旋轉動畫,且如果使用者佈建回調,調用回調介面;設定子功能表的點擊事件。整體就是點擊然後動畫效果~~

7、設定子功能表的點擊事件

/** * 開始菜單動畫,點擊的MenuItem放大消失,其他的縮小消失 * @param item */private void menuItemAnin(int item){for (int i = 0; i < getChildCount() - 1; i++){View childView = getChildAt(i + 1);if (i == item){childView.startAnimation(scaleBigAnim(300));} else{childView.startAnimation(scaleSmallAnim(300));}childView.setClickable(false);childView.setFocusable(false);}}/** * 縮小消失 * @param durationMillis * @return */private Animation scaleSmallAnim(int durationMillis){Animation anim = new ScaleAnimation(1.0f, 0f, 1.0f, 0f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);anim.setDuration(durationMillis);anim.setFillAfter(true);return anim;}/** * 放大,透明度降低 * @param durationMillis * @return */private Animation scaleBigAnim(int durationMillis){AnimationSet animationset = new AnimationSet(true);Animation anim = new ScaleAnimation(1.0f, 4.0f, 1.0f, 4.0f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);Animation alphaAnimation = new AlphaAnimation(1, 0);animationset.addAnimation(anim);animationset.addAnimation(alphaAnimation);animationset.setDuration(durationMillis);animationset.setFillAfter(true);return animationset;}

點擊的功能表項目變大且慢慢透明消失,未點擊的功能表項目縮小消失~有興趣的可以改成自己喜歡的動畫~

註:動畫效果很多借鑒了eoe上那位仁兄的代碼,這類動畫也比較簡單,就不多說了~


好了,剩下就是些getter,setter了~

8、布局檔案:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:zhy="http://schemas.android.com/apk/res/com.example.zhy_arcmenu"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <com.example.zhy_arcmenu.ArcMenu        android:id="@+id/id_arcmenu1"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        zhy:position="left_top"        zhy:radius="130dp" >        <RelativeLayout            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:background="@drawable/composer_button" >            <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_centerInParent="true"                android:src="@drawable/composer_icn_plus" />        </RelativeLayout>        <ImageView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:src="@drawable/composer_camera"            android:tag="Camera" />        <ImageView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:src="@drawable/composer_sun"            android:tag="Sun" />        <ImageView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:src="@drawable/composer_place"            android:tag="Place" />                <ImageView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:src="@drawable/composer_sleep"            android:tag="Sleep" />    </com.example.zhy_arcmenu.ArcMenu>

嗯,第一個元素為按鈕,其他的都是功能表項目了~~喜歡用代碼的,也可以代碼產生,我是比較喜歡布局檔案中寫這些玩意~

9、MainActivity

package com.example.zhy_arcmenu;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.Window;import android.widget.ImageView;import android.widget.Toast;import com.example.zhy_arcmenu.ArcMenu.OnMenuItemClickListener;public class MainActivity extends Activity{private ArcMenu mArcMenuLeftTop;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mArcMenuLeftTop = (ArcMenu) findViewById(R.id.id_arcmenu1);//動態添加一個MenuItemImageView people = new ImageView(this);people.setImageResource(R.drawable.composer_with);people.setTag("People");mArcMenuLeftTop.addView(people);mArcMenuLeftTop.setOnMenuItemClickListener(new OnMenuItemClickListener(){@Overridepublic void onClick(View view, int pos){Toast.makeText(MainActivity.this,pos + ":" + view.getTag(), Toast.LENGTH_SHORT).show();}});}}



結束~~有任何意見歡迎指出~~


源碼點擊下載




Android自訂ViewGroup內的View布局奇怪問題您好問題奇怪TextView尺寸會變小樣解釋:

控制項有兩類非常重要屬性座標:x,y; 尺寸:width,height.
控制項其實些矩形框兩類屬性確定了Canvas上畫出矩形了清楚點
要知道android 確定X,Y和寬高
X,Y:控制項父控制項座標
寬高沒好講矩形寬和高

android通過View onLayout()確定控制項父控制項XY;通過onMeasure()確定控制項寬高想象下控制項樹(xml 布局檔案)從根節點開始根節點XY和寬高通過視窗螢幕大小確定確定了依次調用其子節點onLayout(),onMeasure()來確定子節點父節點座標和尺寸android LayoutInflater整過程了(其視窗系統過程基本樣)

理解應該知道控制項座標和父控制項有關;子控制項尺寸子控制項有fill_parent樣屬性,尺寸也和父控制項有關了
Android 自訂ViewGroup , 能夠實現自適應高度。知道具體代碼好說清般來說viewgroup外面scrollviewviewgroup設定 warp_content應該

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.