├── .gitignore ├── DropDownMenu.iml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── build │ └── outputs │ │ └── apk │ │ └── app-debug.apk ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── yyy │ │ └── djk │ │ └── dropdownmenu │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── yyy │ │ └── djk │ │ └── dropdownmenu │ │ ├── ConstellationAdapter.java │ │ ├── GirdDropDownAdapter.java │ │ ├── ListDropDownAdapter.java │ │ └── MainActivity.java │ └── res │ ├── drawable │ ├── check_bg.xml │ ├── ok_bg.xml │ └── uncheck_bg.xml │ ├── layout │ ├── activity_main.xml │ ├── custom_layout.xml │ ├── item_constellation_layout.xml │ ├── item_default_drop_down.xml │ └── item_list_drop_down.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ ├── drop_down_checked.png │ ├── drop_down_selected_icon.png │ ├── drop_down_unselected_icon.png │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── interger.xml │ ├── strings.xml │ └── styles.xml ├── art ├── download.png └── simple.gif ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── library.iml ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── yyydjk │ │ └── library │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── yyydjk │ │ │ └── library │ │ │ ├── DeviceUtils.java │ │ │ └── DropDownMenu.java │ └── res │ │ ├── anim │ │ ├── dd_mask_in.xml │ │ ├── dd_mask_out.xml │ │ ├── dd_menu_in.xml │ │ └── dd_menu_out.xml │ │ └── values │ │ ├── attrs.xml │ │ └── colors.xml │ └── test │ └── java │ └── com │ └── yyydjk │ └── library │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | /captures 8 | 9 | .idea -------------------------------------------------------------------------------- /DropDownMenu.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://jitpack.io/v/dongjunkun/DropDownMenu.svg)](https://jitpack.io/#dongjunkun/DropDownMenu) 2 | 3 | ## 简介 4 | 一个实用的多条件筛选菜单,在很多App上都能看到这个效果,如美团,爱奇艺电影票等 5 | 6 | 我的博客 [自己造轮子--android常用多条件帅选菜单实现思路(类似美团,爱奇艺电影票下拉菜单)](http://www.jianshu.com/p/d9407f799d2d) 7 | 8 | ## 特色 9 | - 支持多级菜单 10 | - 你可以完全自定义你的菜单样式,我这里只是封装了一些实用的方法,Tab的切换效果,菜单显示隐藏效果等 11 | - 并非用popupWindow实现,无卡顿 12 | 13 | ## ScreenShot 14 | 15 | 16 | Download APK 17 | 18 | 或者扫描二维码 19 | 20 | 21 | 22 | ## Gradle Dependency 23 | 24 | ``` 25 | allprojects { 26 | repositories { 27 | ... 28 | maven { url "https://jitpack.io" } 29 | } 30 | } 31 | 32 | dependencies { 33 | compile 'com.github.dongjunkun:DropDownMenu:1.0.4' 34 | } 35 | ``` 36 | 37 | ## 使用 38 | 添加DropDownMenu 到你的布局文件,如下 39 | ``` 40 | 56 | ``` 57 | 我们只需要在java代码中调用下面的代码 58 | 59 | ``` 60 | //tabs 所有标题,popupViews 所有菜单,contentView 内容 61 | mDropDownMenu.setDropDownMenu(tabs, popupViews, contentView); 62 | ``` 63 | 如果你要了解更多,可以直接看源码 Example 64 | 65 | > 建议拷贝代码到项目中使用,拷贝DropDownMenu.java 以及res下的所有文件即可 66 | 67 | ## 关于我 68 | 简书[dongjunkun](http://www.jianshu.com/users/f07458c1a8f3/latest_articles) 69 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | build/outputs/logs 3 | build/outputs/apk/app-debug-unaligned.apk 4 | build/generated 5 | build/intermediates 6 | build/tmp -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.yyy.djk.dropdownmenu" 9 | minSdkVersion 14 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:25.3.1' 25 | compile 'com.jakewharton:butterknife:6.1.0' 26 | compile project(':library') 27 | } 28 | -------------------------------------------------------------------------------- /app/build/outputs/apk/app-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongjunkun/DropDownMenu/63e308f94282fa23e4a8c043328033913c88214d/app/build/outputs/apk/app-debug.apk -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/yyy/djk/dropdownmenu/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.yyy.djk.dropdownmenu; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/yyy/djk/dropdownmenu/ConstellationAdapter.java: -------------------------------------------------------------------------------- 1 | package com.yyy.djk.dropdownmenu; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.TextView; 9 | 10 | 11 | import java.util.List; 12 | 13 | import butterknife.ButterKnife; 14 | import butterknife.InjectView; 15 | 16 | 17 | public class ConstellationAdapter extends BaseAdapter { 18 | 19 | private Context context; 20 | private List list; 21 | private int checkItemPosition = 0; 22 | 23 | public void setCheckItem(int position) { 24 | checkItemPosition = position; 25 | notifyDataSetChanged(); 26 | } 27 | 28 | public ConstellationAdapter(Context context, List list) { 29 | this.context = context; 30 | this.list = list; 31 | } 32 | 33 | @Override 34 | public int getCount() { 35 | return list.size(); 36 | } 37 | 38 | @Override 39 | public Object getItem(int position) { 40 | return null; 41 | } 42 | 43 | @Override 44 | public long getItemId(int position) { 45 | return position; 46 | } 47 | 48 | @Override 49 | public View getView(int position, View convertView, ViewGroup parent) { 50 | ViewHolder viewHolder; 51 | if (convertView != null) { 52 | viewHolder = (ViewHolder) convertView.getTag(); 53 | } else { 54 | convertView = LayoutInflater.from(context).inflate(R.layout.item_constellation_layout, null); 55 | viewHolder = new ViewHolder(convertView); 56 | convertView.setTag(viewHolder); 57 | } 58 | fillValue(position, viewHolder); 59 | return convertView; 60 | } 61 | 62 | private void fillValue(int position, ViewHolder viewHolder) { 63 | viewHolder.mText.setText(list.get(position)); 64 | if (checkItemPosition != -1) { 65 | if (checkItemPosition == position) { 66 | viewHolder.mText.setTextColor(context.getResources().getColor(R.color.drop_down_selected)); 67 | viewHolder.mText.setBackgroundResource(R.drawable.check_bg); 68 | } else { 69 | viewHolder.mText.setTextColor(context.getResources().getColor(R.color.drop_down_unselected)); 70 | viewHolder.mText.setBackgroundResource(R.drawable.uncheck_bg); 71 | } 72 | } 73 | } 74 | 75 | static class ViewHolder { 76 | @InjectView(R.id.text) 77 | TextView mText; 78 | 79 | ViewHolder(View view) { 80 | ButterKnife.inject(this, view); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/yyy/djk/dropdownmenu/GirdDropDownAdapter.java: -------------------------------------------------------------------------------- 1 | package com.yyy.djk.dropdownmenu; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.TextView; 9 | 10 | 11 | import java.util.List; 12 | 13 | import butterknife.ButterKnife; 14 | import butterknife.InjectView; 15 | 16 | 17 | public class GirdDropDownAdapter extends BaseAdapter { 18 | 19 | private Context context; 20 | private List list; 21 | private int checkItemPosition = 0; 22 | 23 | public void setCheckItem(int position) { 24 | checkItemPosition = position; 25 | notifyDataSetChanged(); 26 | } 27 | 28 | public GirdDropDownAdapter(Context context, List list) { 29 | this.context = context; 30 | this.list = list; 31 | } 32 | 33 | @Override 34 | public int getCount() { 35 | return list.size(); 36 | } 37 | 38 | @Override 39 | public Object getItem(int position) { 40 | return null; 41 | } 42 | 43 | @Override 44 | public long getItemId(int position) { 45 | return position; 46 | } 47 | 48 | @Override 49 | public View getView(int position, View convertView, ViewGroup parent) { 50 | ViewHolder viewHolder; 51 | if (convertView != null) { 52 | viewHolder = (ViewHolder) convertView.getTag(); 53 | } else { 54 | convertView = LayoutInflater.from(context).inflate(R.layout.item_list_drop_down, null); 55 | viewHolder = new ViewHolder(convertView); 56 | convertView.setTag(viewHolder); 57 | } 58 | fillValue(position, viewHolder); 59 | return convertView; 60 | } 61 | 62 | private void fillValue(int position, ViewHolder viewHolder) { 63 | viewHolder.mText.setText(list.get(position)); 64 | if (checkItemPosition != -1) { 65 | if (checkItemPosition == position) { 66 | viewHolder.mText.setTextColor(context.getResources().getColor(R.color.drop_down_selected)); 67 | viewHolder.mText.setCompoundDrawablesWithIntrinsicBounds(null, null, context.getResources().getDrawable(R.mipmap.drop_down_checked), null); 68 | } else { 69 | viewHolder.mText.setTextColor(context.getResources().getColor(R.color.drop_down_unselected)); 70 | viewHolder.mText.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); 71 | } 72 | } 73 | } 74 | 75 | static class ViewHolder { 76 | @InjectView(R.id.text) 77 | TextView mText; 78 | 79 | ViewHolder(View view) { 80 | ButterKnife.inject(this, view); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/yyy/djk/dropdownmenu/ListDropDownAdapter.java: -------------------------------------------------------------------------------- 1 | package com.yyy.djk.dropdownmenu; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.TextView; 9 | 10 | 11 | 12 | import java.util.List; 13 | 14 | import butterknife.ButterKnife; 15 | import butterknife.InjectView; 16 | 17 | 18 | public class ListDropDownAdapter extends BaseAdapter { 19 | 20 | private Context context; 21 | private List list; 22 | private int checkItemPosition = 0; 23 | 24 | public void setCheckItem(int position) { 25 | checkItemPosition = position; 26 | notifyDataSetChanged(); 27 | } 28 | 29 | public ListDropDownAdapter(Context context, List list) { 30 | this.context = context; 31 | this.list = list; 32 | } 33 | 34 | @Override 35 | public int getCount() { 36 | return list.size(); 37 | } 38 | 39 | @Override 40 | public Object getItem(int position) { 41 | return null; 42 | } 43 | 44 | @Override 45 | public long getItemId(int position) { 46 | return position; 47 | } 48 | 49 | @Override 50 | public View getView(int position, View convertView, ViewGroup parent) { 51 | ViewHolder viewHolder; 52 | if (convertView != null) { 53 | viewHolder = (ViewHolder) convertView.getTag(); 54 | } else { 55 | convertView = LayoutInflater.from(context).inflate(R.layout.item_default_drop_down, null); 56 | viewHolder = new ViewHolder(convertView); 57 | convertView.setTag(viewHolder); 58 | } 59 | fillValue(position, viewHolder); 60 | return convertView; 61 | } 62 | 63 | private void fillValue(int position, ViewHolder viewHolder) { 64 | viewHolder.mText.setText(list.get(position)); 65 | if (checkItemPosition != -1) { 66 | if (checkItemPosition == position) { 67 | viewHolder.mText.setTextColor(context.getResources().getColor(R.color.drop_down_selected)); 68 | viewHolder.mText.setBackgroundResource(R.color.check_bg); 69 | } else { 70 | viewHolder.mText.setTextColor(context.getResources().getColor(R.color.drop_down_unselected)); 71 | viewHolder.mText.setBackgroundResource(R.color.white); 72 | } 73 | } 74 | } 75 | 76 | static class ViewHolder { 77 | @InjectView(R.id.text) 78 | TextView mText; 79 | 80 | ViewHolder(View view) { 81 | ButterKnife.inject(this, view); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/yyy/djk/dropdownmenu/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.yyy.djk.dropdownmenu; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.util.TypedValue; 6 | import android.view.Gravity; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.AdapterView; 10 | import android.widget.GridView; 11 | import android.widget.ListView; 12 | import android.widget.TextView; 13 | 14 | import com.yyydjk.library.DropDownMenu; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | 20 | import butterknife.ButterKnife; 21 | import butterknife.InjectView; 22 | 23 | public class MainActivity extends AppCompatActivity { 24 | 25 | @InjectView(R.id.dropDownMenu) DropDownMenu mDropDownMenu; 26 | private String headers[] = {"城市", "年龄", "性别", "星座"}; 27 | private List popupViews = new ArrayList<>(); 28 | 29 | private GirdDropDownAdapter cityAdapter; 30 | private ListDropDownAdapter ageAdapter; 31 | private ListDropDownAdapter sexAdapter; 32 | private ConstellationAdapter constellationAdapter; 33 | 34 | private String citys[] = {"不限", "武汉", "北京", "上海", "成都", "广州", "深圳", "重庆", "天津", "西安", "南京", "杭州"}; 35 | private String ages[] = {"不限", "18岁以下", "18-22岁", "23-26岁", "27-35岁", "35岁以上"}; 36 | private String sexs[] = {"不限", "男", "女"}; 37 | private String constellations[] = {"不限", "白羊座", "金牛座", "双子座", "巨蟹座", "狮子座", "处女座", "天秤座", "天蝎座", "射手座", "摩羯座", "水瓶座", "双鱼座"}; 38 | 39 | private int constellationPosition = 0; 40 | 41 | @Override 42 | protected void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_main); 45 | ButterKnife.inject(this); 46 | initView(); 47 | } 48 | 49 | private void initView() { 50 | //init city menu 51 | final ListView cityView = new ListView(this); 52 | cityAdapter = new GirdDropDownAdapter(this, Arrays.asList(citys)); 53 | cityView.setDividerHeight(0); 54 | cityView.setAdapter(cityAdapter); 55 | 56 | //init age menu 57 | final ListView ageView = new ListView(this); 58 | ageView.setDividerHeight(0); 59 | ageAdapter = new ListDropDownAdapter(this, Arrays.asList(ages)); 60 | ageView.setAdapter(ageAdapter); 61 | 62 | //init sex menu 63 | final ListView sexView = new ListView(this); 64 | sexView.setDividerHeight(0); 65 | sexAdapter = new ListDropDownAdapter(this, Arrays.asList(sexs)); 66 | sexView.setAdapter(sexAdapter); 67 | 68 | //init constellation 69 | final View constellationView = getLayoutInflater().inflate(R.layout.custom_layout, null); 70 | GridView constellation = ButterKnife.findById(constellationView, R.id.constellation); 71 | constellationAdapter = new ConstellationAdapter(this, Arrays.asList(constellations)); 72 | constellation.setAdapter(constellationAdapter); 73 | TextView ok = ButterKnife.findById(constellationView, R.id.ok); 74 | ok.setOnClickListener(new View.OnClickListener() { 75 | @Override 76 | public void onClick(View v) { 77 | mDropDownMenu.setTabText(constellationPosition == 0 ? headers[3] : constellations[constellationPosition]); 78 | mDropDownMenu.closeMenu(); 79 | } 80 | }); 81 | 82 | //init popupViews 83 | popupViews.add(cityView); 84 | popupViews.add(ageView); 85 | popupViews.add(sexView); 86 | popupViews.add(constellationView); 87 | 88 | //add item click event 89 | cityView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 90 | @Override 91 | public void onItemClick(AdapterView parent, View view, int position, long id) { 92 | cityAdapter.setCheckItem(position); 93 | mDropDownMenu.setTabText(position == 0 ? headers[0] : citys[position]); 94 | mDropDownMenu.closeMenu(); 95 | } 96 | }); 97 | 98 | ageView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 99 | @Override 100 | public void onItemClick(AdapterView parent, View view, int position, long id) { 101 | ageAdapter.setCheckItem(position); 102 | mDropDownMenu.setTabText(position == 0 ? headers[1] : ages[position]); 103 | mDropDownMenu.closeMenu(); 104 | } 105 | }); 106 | 107 | sexView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 108 | @Override 109 | public void onItemClick(AdapterView parent, View view, int position, long id) { 110 | sexAdapter.setCheckItem(position); 111 | mDropDownMenu.setTabText(position == 0 ? headers[2] : sexs[position]); 112 | mDropDownMenu.closeMenu(); 113 | } 114 | }); 115 | 116 | constellation.setOnItemClickListener(new AdapterView.OnItemClickListener() { 117 | @Override 118 | public void onItemClick(AdapterView parent, View view, int position, long id) { 119 | constellationAdapter.setCheckItem(position); 120 | constellationPosition = position; 121 | } 122 | }); 123 | 124 | //init context view 125 | TextView contentView = new TextView(this); 126 | contentView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 127 | contentView.setText("内容显示区域"); 128 | contentView.setGravity(Gravity.CENTER); 129 | contentView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20); 130 | 131 | //init dropdownview 132 | mDropDownMenu.setDropDownMenu(Arrays.asList(headers), popupViews, contentView); 133 | } 134 | 135 | @Override 136 | public void onBackPressed() { 137 | //退出activity前关闭菜单 138 | if (mDropDownMenu.isShowing()) { 139 | mDropDownMenu.closeMenu(); 140 | } else { 141 | super.onBackPressed(); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/check_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ok_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/uncheck_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/custom_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_constellation_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_default_drop_down.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_list_drop_down.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 18 | 19 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongjunkun/DropDownMenu/63e308f94282fa23e4a8c043328033913c88214d/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongjunkun/DropDownMenu/63e308f94282fa23e4a8c043328033913c88214d/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/drop_down_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongjunkun/DropDownMenu/63e308f94282fa23e4a8c043328033913c88214d/app/src/main/res/mipmap-xhdpi/drop_down_checked.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/drop_down_selected_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongjunkun/DropDownMenu/63e308f94282fa23e4a8c043328033913c88214d/app/src/main/res/mipmap-xhdpi/drop_down_selected_icon.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/drop_down_unselected_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongjunkun/DropDownMenu/63e308f94282fa23e4a8c043328033913c88214d/app/src/main/res/mipmap-xhdpi/drop_down_unselected_icon.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongjunkun/DropDownMenu/63e308f94282fa23e4a8c043328033913c88214d/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongjunkun/DropDownMenu/63e308f94282fa23e4a8c043328033913c88214d/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ffffff 4 | #cccccc 5 | 6 | #7B1FA2 7 | #111111 8 | 9 | #88888888 10 | #f2f2f2 11 | #9C27B0 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/interger.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 200 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | DropDownMenu 3 | 4 | Hello world! 5 | Settings 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /art/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongjunkun/DropDownMenu/63e308f94282fa23e4a8c043328033913c88214d/art/download.png -------------------------------------------------------------------------------- /art/simple.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongjunkun/DropDownMenu/63e308f94282fa23e4a8c043328033913c88214d/art/simple.gif -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | jcenter() 5 | } 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:2.3.1' 8 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' // Add this line 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | allprojects { 15 | repositories { 16 | jcenter() 17 | } 18 | } 19 | 20 | dependencies { 21 | } 22 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dongjunkun/DropDownMenu/63e308f94282fa23e4a8c043328033913c88214d/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Apr 11 16:01:41 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | 4 | group = 'com.github.dongjunkun' 5 | 6 | android { 7 | compileSdkVersion 25 8 | buildToolsVersion "25.0.2" 9 | 10 | defaultConfig { 11 | minSdkVersion 11 12 | targetSdkVersion 25 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | compile fileTree(dir: 'libs', include: ['*.jar']) 26 | testCompile 'junit:junit:4.12' 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | } 29 | -------------------------------------------------------------------------------- /library/library.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /library/src/androidTest/java/com/yyydjk/library/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.yyydjk.library; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /library/src/main/java/com/yyydjk/library/DeviceUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Peng fei Pan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.yyydjk.library; 18 | 19 | import android.annotation.TargetApi; 20 | import android.content.Context; 21 | import android.graphics.Point; 22 | import android.os.Build; 23 | import android.view.Display; 24 | import android.view.WindowManager; 25 | 26 | /** 27 | * 设备工具箱,提供与设备硬件相关的工具方法 28 | */ 29 | public class DeviceUtils { 30 | 31 | /** 32 | * 获取屏幕尺寸 33 | */ 34 | @SuppressWarnings("deprecation") 35 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) 36 | public static Point getScreenSize(Context context){ 37 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 38 | Display display = windowManager.getDefaultDisplay(); 39 | if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2){ 40 | return new Point(display.getWidth(), display.getHeight()); 41 | }else{ 42 | Point point = new Point(); 43 | display.getSize(point); 44 | return point; 45 | } 46 | } 47 | 48 | 49 | } -------------------------------------------------------------------------------- /library/src/main/java/com/yyydjk/library/DropDownMenu.java: -------------------------------------------------------------------------------- 1 | package com.yyydjk.library; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.support.annotation.NonNull; 6 | import android.text.TextUtils; 7 | import android.util.AttributeSet; 8 | import android.util.DisplayMetrics; 9 | import android.util.TypedValue; 10 | import android.view.Gravity; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.view.animation.AnimationUtils; 14 | import android.widget.FrameLayout; 15 | import android.widget.LinearLayout; 16 | import android.widget.TextView; 17 | 18 | import java.util.List; 19 | 20 | 21 | /** 22 | * Created by dongjunkun on 2015/6/17. 23 | */ 24 | public class DropDownMenu extends LinearLayout { 25 | 26 | //顶部菜单布局 27 | private LinearLayout tabMenuView; 28 | //底部容器,包含popupMenuViews,maskView 29 | private FrameLayout containerView; 30 | //弹出菜单父布局 31 | private FrameLayout popupMenuViews; 32 | //遮罩半透明View,点击可关闭DropDownMenu 33 | private View maskView; 34 | //tabMenuView里面选中的tab位置,-1表示未选中 35 | private int current_tab_position = -1; 36 | 37 | //分割线颜色 38 | private int dividerColor = 0xffcccccc; 39 | //tab选中颜色 40 | private int textSelectedColor = 0xff890c85; 41 | //tab未选中颜色 42 | private int textUnselectedColor = 0xff111111; 43 | //遮罩颜色 44 | private int maskColor = 0x88888888; 45 | //tab字体大小 46 | private int menuTextSize = 14; 47 | 48 | //tab选中图标 49 | private int menuSelectedIcon; 50 | //tab未选中图标 51 | private int menuUnselectedIcon; 52 | 53 | private float menuHeighPercent = 0.5f; 54 | 55 | 56 | public DropDownMenu(Context context) { 57 | super(context, null); 58 | } 59 | 60 | public DropDownMenu(Context context, AttributeSet attrs) { 61 | this(context, attrs, 0); 62 | } 63 | 64 | public DropDownMenu(Context context, AttributeSet attrs, int defStyleAttr) { 65 | super(context, attrs, defStyleAttr); 66 | 67 | setOrientation(VERTICAL); 68 | 69 | //为DropDownMenu添加自定义属性 70 | int menuBackgroundColor = 0xffffffff; 71 | int underlineColor = 0xffcccccc; 72 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DropDownMenu); 73 | underlineColor = a.getColor(R.styleable.DropDownMenu_ddunderlineColor, underlineColor); 74 | dividerColor = a.getColor(R.styleable.DropDownMenu_dddividerColor, dividerColor); 75 | textSelectedColor = a.getColor(R.styleable.DropDownMenu_ddtextSelectedColor, textSelectedColor); 76 | textUnselectedColor = a.getColor(R.styleable.DropDownMenu_ddtextUnselectedColor, textUnselectedColor); 77 | menuBackgroundColor = a.getColor(R.styleable.DropDownMenu_ddmenuBackgroundColor, menuBackgroundColor); 78 | maskColor = a.getColor(R.styleable.DropDownMenu_ddmaskColor, maskColor); 79 | menuTextSize = a.getDimensionPixelSize(R.styleable.DropDownMenu_ddmenuTextSize, menuTextSize); 80 | menuSelectedIcon = a.getResourceId(R.styleable.DropDownMenu_ddmenuSelectedIcon, menuSelectedIcon); 81 | menuUnselectedIcon = a.getResourceId(R.styleable.DropDownMenu_ddmenuUnselectedIcon, menuUnselectedIcon); 82 | menuHeighPercent = a.getFloat(R.styleable.DropDownMenu_ddmenuMenuHeightPercent,menuHeighPercent); 83 | a.recycle(); 84 | 85 | //初始化tabMenuView并添加到tabMenuView 86 | tabMenuView = new LinearLayout(context); 87 | LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); 88 | tabMenuView.setOrientation(HORIZONTAL); 89 | tabMenuView.setBackgroundColor(menuBackgroundColor); 90 | tabMenuView.setLayoutParams(params); 91 | addView(tabMenuView, 0); 92 | 93 | //为tabMenuView添加下划线 94 | View underLine = new View(getContext()); 95 | underLine.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dpTpPx(1.0f))); 96 | underLine.setBackgroundColor(underlineColor); 97 | addView(underLine, 1); 98 | 99 | //初始化containerView并将其添加到DropDownMenu 100 | containerView = new FrameLayout(context); 101 | containerView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); 102 | addView(containerView, 2); 103 | 104 | } 105 | 106 | /** 107 | * 初始化DropDownMenu 108 | * 109 | * @param tabTexts 110 | * @param popupViews 111 | * @param contentView 112 | */ 113 | public void setDropDownMenu(@NonNull List tabTexts, @NonNull List popupViews, @NonNull View contentView) { 114 | if (tabTexts.size() != popupViews.size()) { 115 | throw new IllegalArgumentException("params not match, tabTexts.size() should be equal popupViews.size()"); 116 | } 117 | 118 | for (int i = 0; i < tabTexts.size(); i++) { 119 | addTab(tabTexts, i); 120 | } 121 | containerView.addView(contentView, 0); 122 | 123 | maskView = new View(getContext()); 124 | maskView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); 125 | maskView.setBackgroundColor(maskColor); 126 | maskView.setOnClickListener(new OnClickListener() { 127 | @Override 128 | public void onClick(View v) { 129 | closeMenu(); 130 | } 131 | }); 132 | containerView.addView(maskView, 1); 133 | maskView.setVisibility(GONE); 134 | if (containerView.getChildAt(2) != null){ 135 | containerView.removeViewAt(2); 136 | } 137 | 138 | popupMenuViews = new FrameLayout(getContext()); 139 | popupMenuViews.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) (DeviceUtils.getScreenSize(getContext()).y*menuHeighPercent))); 140 | popupMenuViews.setVisibility(GONE); 141 | containerView.addView(popupMenuViews, 2); 142 | 143 | for (int i = 0; i < popupViews.size(); i++) { 144 | popupViews.get(i).setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 145 | popupMenuViews.addView(popupViews.get(i), i); 146 | } 147 | 148 | } 149 | 150 | private void addTab(@NonNull List tabTexts, int i) { 151 | final TextView tab = new TextView(getContext()); 152 | tab.setSingleLine(); 153 | tab.setEllipsize(TextUtils.TruncateAt.END); 154 | tab.setGravity(Gravity.CENTER); 155 | tab.setTextSize(TypedValue.COMPLEX_UNIT_PX,menuTextSize); 156 | tab.setLayoutParams(new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f)); 157 | tab.setTextColor(textUnselectedColor); 158 | tab.setCompoundDrawablesWithIntrinsicBounds(null, null, getResources().getDrawable(menuUnselectedIcon), null); 159 | tab.setText(tabTexts.get(i)); 160 | tab.setPadding(dpTpPx(5), dpTpPx(12), dpTpPx(5), dpTpPx(12)); 161 | //添加点击事件 162 | tab.setOnClickListener(new OnClickListener() { 163 | @Override 164 | public void onClick(View v) { 165 | switchMenu(tab); 166 | } 167 | }); 168 | tabMenuView.addView(tab); 169 | //添加分割线 170 | if (i < tabTexts.size() - 1) { 171 | View view = new View(getContext()); 172 | view.setLayoutParams(new LayoutParams(dpTpPx(0.5f), ViewGroup.LayoutParams.MATCH_PARENT)); 173 | view.setBackgroundColor(dividerColor); 174 | tabMenuView.addView(view); 175 | } 176 | } 177 | 178 | /** 179 | * 改变tab文字 180 | * 181 | * @param text 182 | */ 183 | public void setTabText(String text) { 184 | if (current_tab_position != -1) { 185 | ((TextView) tabMenuView.getChildAt(current_tab_position)).setText(text); 186 | } 187 | } 188 | 189 | public void setTabClickable(boolean clickable) { 190 | for (int i = 0; i < tabMenuView.getChildCount(); i = i + 2) { 191 | tabMenuView.getChildAt(i).setClickable(clickable); 192 | } 193 | } 194 | 195 | /** 196 | * 关闭菜单 197 | */ 198 | public void closeMenu() { 199 | if (current_tab_position != -1) { 200 | ((TextView) tabMenuView.getChildAt(current_tab_position)).setTextColor(textUnselectedColor); 201 | ((TextView) tabMenuView.getChildAt(current_tab_position)).setCompoundDrawablesWithIntrinsicBounds(null, null, 202 | getResources().getDrawable(menuUnselectedIcon), null); 203 | popupMenuViews.setVisibility(View.GONE); 204 | popupMenuViews.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.dd_menu_out)); 205 | maskView.setVisibility(GONE); 206 | maskView.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.dd_mask_out)); 207 | current_tab_position = -1; 208 | } 209 | 210 | } 211 | 212 | /** 213 | * DropDownMenu是否处于可见状态 214 | * 215 | * @return 216 | */ 217 | public boolean isShowing() { 218 | return current_tab_position != -1; 219 | } 220 | 221 | /** 222 | * 切换菜单 223 | * 224 | * @param target 225 | */ 226 | private void switchMenu(View target) { 227 | System.out.println(current_tab_position); 228 | for (int i = 0; i < tabMenuView.getChildCount(); i = i + 2) { 229 | if (target == tabMenuView.getChildAt(i)) { 230 | if (current_tab_position == i) { 231 | closeMenu(); 232 | } else { 233 | if (current_tab_position == -1) { 234 | popupMenuViews.setVisibility(View.VISIBLE); 235 | popupMenuViews.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.dd_menu_in)); 236 | maskView.setVisibility(VISIBLE); 237 | maskView.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.dd_mask_in)); 238 | popupMenuViews.getChildAt(i / 2).setVisibility(View.VISIBLE); 239 | } else { 240 | popupMenuViews.getChildAt(i / 2).setVisibility(View.VISIBLE); 241 | } 242 | current_tab_position = i; 243 | ((TextView) tabMenuView.getChildAt(i)).setTextColor(textSelectedColor); 244 | ((TextView) tabMenuView.getChildAt(i)).setCompoundDrawablesWithIntrinsicBounds(null, null, 245 | getResources().getDrawable(menuSelectedIcon), null); 246 | } 247 | } else { 248 | ((TextView) tabMenuView.getChildAt(i)).setTextColor(textUnselectedColor); 249 | ((TextView) tabMenuView.getChildAt(i)).setCompoundDrawablesWithIntrinsicBounds(null, null, 250 | getResources().getDrawable(menuUnselectedIcon), null); 251 | popupMenuViews.getChildAt(i / 2).setVisibility(View.GONE); 252 | } 253 | } 254 | } 255 | 256 | public int dpTpPx(float value) { 257 | DisplayMetrics dm = getResources().getDisplayMetrics(); 258 | return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, dm) + 0.5); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /library/src/main/res/anim/dd_mask_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /library/src/main/res/anim/dd_mask_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /library/src/main/res/anim/dd_menu_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /library/src/main/res/anim/dd_menu_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /library/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /library/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ffffff 4 | #cccccc 5 | 6 | #7B1FA2 7 | #111111 8 | 9 | #88888888 10 | #f2f2f2 11 | #9C27B0 12 | -------------------------------------------------------------------------------- /library/src/test/java/com/yyydjk/library/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.yyydjk.library; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':library' 2 | --------------------------------------------------------------------------------