編輯:關於Android編程
本來很懶,但是還是會忍不住的寫下這有用既沒有用的所謂技術博客,希望會給你帶來有所啟發,因為這樣的功能,寫的人很多,也是為了自己能夠理解的夠透徹,也是為了大家也能更好的理解吧,其實自定義控件,也就是那麼回事,沒有什麼可難的,好吧,可能是這個太簡單了。
這是58,趕集都用到的控件,這樣的控件,自定義起來其實也並不難,自己項目當中用到了這個,於是就研究了一番,可能在你的項目當中也有可能會遇到這樣的需求,正好,也可以來研究下,說真的,今天你見到的這個效果,比58,和趕集做的效果 要好多了,他們做的真的很low。
先來看下他長什麼樣子吧
對,就是這個樣子,今天我們就來看看到底是怎麼做出來的。
整體的所有布局都是用代碼創建出來的,不是寫死在布局文件裡的,這樣會更靈活
創建菜單欄,就是地址呀,年齡,性別的布局,水平的LinearLayout 向菜單欄裡添加布局,就是一個簡單的TextView,和分割線,然後將該布局添加至父控件中,就是最外層布局 創建下劃線,再添加至父控件中 創建內容顯示區,注意,內容顯示區包括上圖中的顯示的內容(這裡是內容區域),還有點擊下拉菜單出來的布局,和半透明的背景,都是在內容區的,他的布局是FrameLayout,注意FrameLayout的特點,有不熟悉他的特點的可以先查一下他的特點,這樣就會更好的理解這個控件的繪制了。 內容顯示區為FrameLayout,他裡面包括3部分,最裡層是真正的的內容顯示區,就是上圖中看到的文字(這裡是內容區域),中間層是半透明區域,就是大家看到的變暗的背景,最外層是點擊下拉菜單之後彈出來的內容下面將會帶你用代碼我做這個控件的思路
public class DropDownMenu extends LinearLayout { //頂部菜單布局 private LinearLayout tabMenuView; //底部容器,包含popupMenuViews,maskView private FrameLayout containerView; //彈出菜單父布局 private FrameLayout popupMenuViews; //遮罩半透明View,點擊可關閉DropDownMenu private View maskView; //tabMenuView裡面選中的tab位置,-1表示未選中 private int current_tab_position = -1; //分割線顏色 private int dividerColor = 0xffcccccc; //tab選中顏色 private int textSelectedColor = 0xff890c85; //tab未選中顏色 private int textUnselectedColor = 0xff111111; //遮罩顏色 private int maskColor = 0x88888888; //tab字體大小 private int menuTextSize = 14; //tab選中圖標 private int menuSelectedIcon; //tab未選中圖標 private int menuUnselectedIcon; public DropDownMenu(Context context) { super(context, null); } public DropDownMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DropDownMenu(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setOrientation(VERTICAL); //為DropDownMenu添加自定義屬性 int menuBackgroundColor = 0xffffffff; int underlineColor = 0xffcccccc; TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DropDownMenu); underlineColor = a.getColor(R.styleable.DropDownMenu_underlineColor, underlineColor); dividerColor = a.getColor(R.styleable.DropDownMenu_dividerColor, dividerColor); textSelectedColor = a.getColor(R.styleable.DropDownMenu_textSelectedColor, textSelectedColor); textUnselectedColor = a.getColor(R.styleable.DropDownMenu_textUnselectedColor, textUnselectedColor); menuBackgroundColor = a.getColor(R.styleable.DropDownMenu_menuBackgroundColor, menuBackgroundColor); maskColor = a.getColor(R.styleable.DropDownMenu_maskColor, maskColor); menuTextSize = a.getDimensionPixelSize(R.styleable.DropDownMenu_menuTextSize, menuTextSize); menuSelectedIcon = a.getResourceId(R.styleable.DropDownMenu_menuSelectedIcon, menuSelectedIcon); menuUnselectedIcon = a.getResourceId(R.styleable.DropDownMenu_menuUnselectedIcon, menuUnselectedIcon); a.recycle(); //初始化tabMenuView並添加到tabMenuView tabMenuView = new LinearLayout(context); LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); tabMenuView.setOrientation(HORIZONTAL); tabMenuView.setBackgroundColor(menuBackgroundColor); tabMenuView.setLayoutParams(params); addView(tabMenuView, 0); //為tabMenuView添加下劃線 View underLine = new View(getContext()); underLine.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dpTpPx(1.0f))); underLine.setBackgroundColor(underlineColor); addView(underLine, 1); //初始化containerView並將其添加到DropDownMenu containerView = new FrameLayout(context); containerView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); addView(containerView, 2); } }
注意下面的成員變量
private LinearLayout tabMenuView;////頂部菜單布局
private FrameLayout containerView;//底部容器,包含popupMenuViews,maskView(暗色背景)
private FrameLayout popupMenuViews;//彈出菜單父布局
private View maskView;//遮罩半透明View,點擊可關閉DropDownMenu
public class DropDownMenu extends LinearLayout {}
也就說整體的布局是繼承自LinearLayout的
setOrientation(VERTICAL);//在第三個構造方法裡指定了整體布局的方向
完成了以上步驟,其實大體上的布局差不多都搭好了,下面就是填充子布局和數據的問題了
/** * 初始化DropDownMenu * * @param tabTexts * @param popupViews * @param contentView */ public void setDropDownMenu(@NonNull ListtabTexts, @NonNull List popupViews, @NonNull View contentView) { if (tabTexts.size() != popupViews.size()) { throw new IllegalArgumentException("params not match, tabTexts.size() should be equal popupViews.size()"); } for (int i = 0; i < tabTexts.size(); i++) { addTab(tabTexts, i); } containerView.addView(contentView, 0); maskView = new View(getContext()); maskView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); maskView.setBackgroundColor(maskColor); maskView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { closeMenu(); } }); containerView.addView(maskView, 1); maskView.setVisibility(GONE); popupMenuViews = new FrameLayout(getContext()); popupMenuViews.setVisibility(GONE); containerView.addView(popupMenuViews, 2); for (int i = 0; i < popupViews.size(); i++) { popupViews.get(i).setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); popupMenuViews.addView(popupViews.get(i), i); } }
這一步是對外暴露的方法,專門給菜單欄,填充數據,還有為內容區填充布局的,布局可以填充任何布局,很靈活
addTab(tabTexts, i);這個方法,下面會給出代碼,用來給菜單欄添加布局和菜單欄的分割線的
containerView.addView(contentView, 0);//將傳入的內容布局添加到內容區(默認是顯示的)
containerView.addView(maskView, 1);//添加暗色背景(默認是GONG的)
containerView.addView(popupMenuViews, 2);//將點擊菜單欄後顯示的布局,注意,這個布局裡也有子布局,那就是listview,所以現在是講父布局添加到內容區的(默認是GONG的)
popupMenuViews.addView(popupViews.get(i), i);//這一步才是為點擊菜單欄後顯示的布局填充子布局的,也就是listview
private void addTab(@NonNull ListtabTexts, int i) { final TextView tab = new TextView(getContext()); tab.setSingleLine(); tab.setEllipsize(TextUtils.TruncateAt.END); tab.setGravity(Gravity.CENTER); tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, menuTextSize); tab.setLayoutParams(new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f)); tab.setTextColor(textUnselectedColor); tab.setCompoundDrawablesWithIntrinsicBounds(null, null, null, getResources().getDrawable(menuUnselectedIcon)); tab.setText(tabTexts.get(i)); tab.setPadding(dpTpPx(5), dpTpPx(12), dpTpPx(5), dpTpPx(12)); //添加點擊事件 tab.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { switchMenu(tab); } }); tabMenuView.addView(tab); //添加分割線 if (i < tabTexts.size() - 1) { View view = new View(getContext()); LayoutParams layoutParams = new LayoutParams(dpTpPx(0.5f), dpTpPx(25)); layoutParams.gravity = Gravity.CENTER_VERTICAL; view.setLayoutParams(layoutParams); view.setBackgroundColor(dividerColor); tabMenuView.addView(view); } }
這一步是為菜單欄添加布局,和分割線的,還有為菜單欄添加點擊事件,沒有什麼可說的
/** * 切換菜單 * * @param view */ private void switchMenu(View view) { for (int i = 0; i < tabMenuView.getChildCount(); i = i + 2) { if (view == tabMenuView.getChildAt(i)) { if (current_tab_position == i) { closeMenu(); } else { if (current_tab_position == -1) { popupMenuViews.setVisibility(View.VISIBLE); popupMenuViews.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.dd_menu_in)); maskView.setVisibility(VISIBLE); maskView.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.dd_mask_in)); popupMenuViews.getChildAt(i / 2).setVisibility(View.VISIBLE); } else { popupMenuViews.getChildAt(i / 2).setVisibility(View.VISIBLE); } current_tab_position = i; ((TextView) tabMenuView.getChildAt(i)).setTextColor(textSelectedColor); ((TextView) tabMenuView.getChildAt(i)).setCompoundDrawablesWithIntrinsicBounds(null, null, null, getResources().getDrawable(menuSelectedIcon)); } } else { //設置其他未點擊tab的狀態 ((TextView) tabMenuView.getChildAt(i)).setTextColor(textUnselectedColor); ((TextView) tabMenuView.getChildAt(i)).setCompoundDrawablesWithIntrinsicBounds(null, null, null, getResources().getDrawable(menuUnselectedIcon)); popupMenuViews.getChildAt(i / 2).setVisibility(View.GONE); } } }
這一步是切換菜單時要做的,只是改變其他狀態而已
做到這,也就意味著,自定義控件寫完了,下面就是如何用他了
將上面的布局copy到你的布局裡就ok啦
自定義的屬性
結束
ok,做到這,就大功告成了,具體的代碼還是以我的Demo為主下面是下載鏈接
DropDownMenuDemo
本文參考了manymore13文章邏輯,在此基礎上做了改進:1.可定義最大行數2.定義每行顯示幾張3.當圖片數量過多時設置更多圖片由於個人較懶,去掉了xml配置屬性,所有
Animations的第二種使用方法
1在res文件夾下新建一個anim文件夾
2.創建xml文件,並首先加入set標簽,改標簽如下:
系列回顧:本系列主要從開發的角度介紹UiAutomator的使用,總共包括三篇: 基礎入門: Android自動化測試之UiAutomator(一) 技巧篇: Andro
微信開放平台和公眾平台的區別1.公眾平台面向的時普通的用戶,比如自媒體和媒體,企業官方微信公眾賬號運營人員使用,當然你所在的團隊或者公司有實力去開發一些內容,也可以調用公