栗子慣例戴涝,先上GIF
使用姿勢(shì)
引入
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
compile 'com.github.FJ917:FJMtSortButton:v1.1'
}
使用
v1.1版本(最新)
- 新增自定義控件DynamicSoreView。實(shí)現(xiàn)通過(guò)服務(wù)器獲取的數(shù)據(jù)场梆,動(dòng)態(tài)添加按鈕日月,可以設(shè)置每頁(yè)顯示的個(gè)數(shù)。
- 修改了v1.0版本里的屬性設(shè)置方式宛瞄,也支持原來(lái)的設(shè)置方式瘫辩,詳細(xì)參考v1.0版本使用方法
xml
<fj.mtsortbutton.lib.DynamicSoreView
android:id="@+id/dynamicSoreView"
android:background="#ffffff"
app:SoreRadioSelect="@drawable/radio1"
app:SoreRadioUnselected="@drawable/radio2"
app:SoreNumber="6"
app:SoreDistance="20"
android:layout_width="match_parent"
android:layout_height="170dp"/>
java
private void data(){
buttonList = setData();//模擬服務(wù)器獲取到的按鈕列表
//設(shè)置界面監(jiān)聽(tīng)
dynamicSoreView.setiDynamicSore(this);
//控件相關(guān)設(shè)置
dynamicSoreView.setGridView(R.layout.viewpager_page).init(buttonList);
}
@Override
public void setGridView(View view, final int type, List data) {
List<ButtonModel> buttonModels= data;
GridView gridView = (GridView) view.findViewById(R.id.gridView);
dynamicSoreView.setNumColumns(gridView);
SortButtonAdapter adapter = new SortButtonAdapter(this,buttonModels);
gridView.setAdapter(adapter);
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(context,"第"+type+"頁(yè)"+position,Toast.LENGTH_LONG).show();
}
});
}
v1.0版本
xml
<!--原用法 -->
<fj.mtsortbutton.lib.SoreButton
android:id="@+id/soreButton"
android:background="#ffffff"
android:layout_width="match_parent"
android:layout_height="170dp"/>
<!-- 修改后用法 -->
<fj.mtsortbutton.lib.SoreButton
android:id="@+id/soreButton"
android:background="#ffffff"
app:SoreRadioSelect="@drawable/radio1"
app:SoreRadioUnselected="@drawable/radio2"
app:SoreDistance="20"
android:layout_width="match_parent"
android:layout_height="170dp"/>
java
//設(shè)置界面監(jiān)聽(tīng)
soreButton.setViewControl(this);
//添加界面到list
list = new ArrayList<>();
list.add(R.layout.viewpager_page);
list.add(R.layout.viewpager_page);
list.add(R.layout.viewpager_page_text);
//控件相關(guān)設(shè)置
//原用法
soreButton
//設(shè)置選中和未選中指示器圖標(biāo)
.setIndicator(R.drawable.radio1,R.drawable.radio2)
//設(shè)置指示器半間距px
.setDistance(10)
//設(shè)置view組
.setView(list)
.init();
//修改后用法
//說(shuō)明:廢棄了設(shè)置參數(shù)的方法,但是也可以用,只是建議在xml設(shè)置
soreButton.setView(list).init();
將layout的布局add進(jìn)去list中伐厌,然后調(diào)用setView方法把list傳過(guò)去承绸,還提供了設(shè)置指示器圖標(biāo)的方法,以及指示器間距的方法挣轨,最后必須調(diào)用初始化方法init進(jìn)行初始化
這是其中的一個(gè)layout布局军熏,其實(shí)這里可以添加任意布局文件進(jìn)去,是不是比美團(tuán)更靈活呢卷扮?
viewpager_page.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp"
android:background="#ffffff" >
<GridView
android:id="@+id/gridView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:horizontalSpacing="5dp"
android:numColumns="5"
android:scrollbars="none"
android:stretchMode="columnWidth"
android:verticalSpacing="10dp" />
</LinearLayout>
設(shè)置soreButton監(jiān)聽(tīng)事件(具體的可以參考GIt里面的Demo)
public class MainActivity extends AppCompatActivity implements ViewControl {
private SoreButton soreButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
soreButton = (SoreButton) findViewById(R.id.soreButton);
soreButton.setViewControl(this);
}
@Override
public void setView(View view, final int type) {
//這里會(huì)返回前面設(shè)置進(jìn)去的View及對(duì)應(yīng)的type,然后就可以進(jìn)行操作了
}
}
劃重點(diǎn):上面的list中可以傳入任意layout布局荡澎,然后通過(guò)接口回掉拿到View。比美團(tuán)更加靈活晤锹。
好了使用姿勢(shì)已經(jīng)講完了摩幔,伸手可以撤退了~ 接下來(lái)開(kāi)始講講實(shí)現(xiàn)原理
實(shí)現(xiàn)原理
需求分析
界面:分為兩部分,上面的按鈕以及下面的指示點(diǎn)鞭铆。上面使用ViewPager
作為View的容器或衡,下面的指示器用LinearLayout
將動(dòng)態(tài)創(chuàng)建的ImageView
添加進(jìn)去。
ViewPager
中的按鈕使用GridView
來(lái)做车遂,當(dāng)然這一部分我們需要做的靈活些封断,不一定只放按鈕,也可以方其他View舶担,超越美團(tuán)坡疼,//手動(dòng)滑稽。
重點(diǎn):通過(guò)自定義組合控件的方式來(lái)進(jìn)行封裝衣陶,方便以后的使用柄瑰。
開(kāi)始封裝
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp" />
<LinearLayout
android:layout_below="@+id/viewPager"
android:id="@+id/llIndicator"
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="#ffffff"
android:gravity="center"
android:orientation="horizontal" >
</LinearLayout>
</LinearLayout>
和普通的布局沒(méi)啥區(qū)別,外層LinearLayout
垂直布局剪况,其中有ViewPager
作為滑動(dòng)切換的容器教沾,內(nèi)部的LinearLayout
作為指示器容器。
SoreButton(自定義控件)
設(shè)置了默認(rèn)值變量拯欧,以及控件和接口定義
Context mContext;
private ViewPager viewPager;
private LinearLayout llIndicator;
//選中圖片
private int RadioSelect = R.drawable.radio_select;
//未選中圖片
private int RadioUnselected = R.drawable.radio_unselected;
//圓點(diǎn)間距
private int distance = 10;
List<View> listSoreView = new ArrayList<>();
View soreView;
private List<Integer> listView;
//接口
private ViewControl viewControl;
//設(shè)置接口
public void setViewControl(ViewControl viewControl) {
this.viewControl = viewControl;
}
拿到了自定義控件的布局中ViewPager和LinearLayout,并且設(shè)置了一個(gè)空布局财骨。
public SoreButton(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
LayoutInflater.from(context).inflate(R.layout.anfq_sore_button, this, true);
viewPager = (ViewPager) findViewById(R.id.viewPager);
llIndicator = (LinearLayout) findViewById(R.id.llIndicator);
//設(shè)置空布局
listView = new ArrayList<>();
listView.add(R.layout.viewpager_default);
}
對(duì)外提供的參數(shù)設(shè)置方法镐作,當(dāng)調(diào)用了init()方法后,會(huì)調(diào)用initViewPager方法進(jìn)行ViewPager的初始化隆箩。(v1.1版本后建議使用xml屬性設(shè)置)
/**
* 設(shè)置圓點(diǎn)距離
* @param distance --距離
* @return
*/
public SoreButton setDistance(int distance){
this.distance = distance;
return this;
}
/**
* 設(shè)置指示器圖片
* @param radioSelect --選中圖片
* @param radioUnselected --未選中圖片
* @return
*/
public SoreButton setIndicator(int radioSelect,int radioUnselected){
//選中圖片
RadioSelect = radioSelect;
//未選中圖片
RadioUnselected = radioUnselected;
return this;
}
/**
* 設(shè)置view
* @param listView --view
* @return
*/
public SoreButton setView(List<Integer> listView){
this.listView = listView;
return this;
}
/**
* 設(shè)置初始化
*/
public SoreButton init(){
initViewPager();
return this;
}
接下來(lái)我們看看initViewPager中都做了那些操作
//初始化ViewPager
private void initViewPager(){
listSoreView = new ArrayList<>();
LayoutInflater layoutInflater = (LayoutInflater)
mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
int size = listView.size();
for (int i = 0; i < size; i++) {
//循環(huán)拿到傳入的View
soreView = layoutInflater.inflate(listView.get(i), null);
//通過(guò)接口回掉的形式返回當(dāng)前的View,實(shí)現(xiàn)接口后開(kāi)源拿到每個(gè)View然后進(jìn)行操作
if (viewControl!=null){
viewControl.setView(soreView,i);
}
//將獲取到的View添加到List中
listSoreView.add(soreView);
}
//設(shè)置viewPager的Adapter
viewPager.setAdapter(new ViewPagerAdapter(listSoreView));
//初始化LinearLayout该贾,加入指示器
initLinearLayout(viewPager, size, llIndicator);
}
因?yàn)橹罢{(diào)用了setView
方法,傳入了一組布局捌臊,我們通過(guò)循環(huán)來(lái)拿到這組View并添加到list中,然后設(shè)置到viewPager的Adapter
杨蛋,然后調(diào)用initLinearLayout
方法初始化指示器。
接下來(lái)我們來(lái)看initLinearLayout中又做了那些操作
/**
* 設(shè)置指示器,設(shè)置ViewPager滑動(dòng)事件監(jiān)聽(tīng)
* @param viewPager --ViewPager
* @param pageSize --View的頁(yè)數(shù)
* @param linearLayout --LinearLayout
*/
private void initLinearLayout(ViewPager viewPager, int pageSize, LinearLayout linearLayout) {
//定義數(shù)組放置指示器的點(diǎn)逞力,pageSize是View個(gè)數(shù)
final ImageView[] imageViews = new ImageView[pageSize];
for (int i = 0; i < pageSize; i++) {
//創(chuàng)建ImageView
ImageView image = new ImageView(mContext);
//將ImageView放入數(shù)組
imageViews[i] = image;
//默認(rèn)選中第一個(gè)
if (i == 0) {
//選中的點(diǎn)
image.setImageResource(RadioSelect);
} else {
//未選中的點(diǎn)
image.setImageResource(RadioUnselected);
}
//設(shè)置寬高
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(distance, 0, distance, 0);
//將點(diǎn)添加到LinearLayout中
linearLayout.addView(image, params);
}
//ViewPager的滑動(dòng)事件
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrollStateChanged(int arg0) {}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {}
@Override
public void onPageSelected(int arg0) {
//arg0當(dāng)前ViewPager
for (int i = 0; i < imageViews.length; i++) {
//設(shè)置為選中的點(diǎn)
imageViews[arg0].setImageResource(RadioSelect);
//判斷當(dāng)前的點(diǎn)i如果不等于當(dāng)前頁(yè)的話就設(shè)置為未選中
if (arg0 != i) {
imageViews[i].setImageResource(RadioUnselected);
}
}
}
});
}
定義了一個(gè)數(shù)組曙寡,用來(lái)放置指示器的點(diǎn),通過(guò)循環(huán)pageSize
來(lái)動(dòng)態(tài)創(chuàng)建ImageView
寇荧,然后判斷i來(lái)將第一頁(yè)的ImageView
設(shè)置為選中的點(diǎn)举庶,其余設(shè)置為未選中的點(diǎn)。接著設(shè)置了寬高揩抡,然后添加到LinearLayout中户侥。
當(dāng)然這樣還不行,在viewPager
滑動(dòng)的時(shí)候我們得更新指示器上的點(diǎn)
我們對(duì)ViewPager
設(shè)置了監(jiān)聽(tīng)事件setOnPageChangeListener
峦嗤,在滑動(dòng)的時(shí)候會(huì)調(diào)用onPageSelected
蕊唐,在這里可以拿到當(dāng)前頁(yè),之后我們通過(guò)循環(huán)剛剛的數(shù)組烁设,將當(dāng)前頁(yè)對(duì)應(yīng)的點(diǎn)設(shè)置為選中圖標(biāo)替梨,不等于當(dāng)前頁(yè)的設(shè)置為未選中的點(diǎn)。
然后就沒(méi)有然后了署尤,封裝完成~
這樣就可以通過(guò)上面所說(shuō)的方式耙替,來(lái)使用這個(gè)自定義控件實(shí)現(xiàn)仿美團(tuán)的效果,而且不止是仿美團(tuán)的效果曹体,可以傳入其他View俗扇,來(lái)實(shí)現(xiàn)其他效果。比如:稍加改動(dòng)布局的話可以作為應(yīng)用的啟動(dòng)引導(dǎo)頁(yè)箕别。
總結(jié)
通過(guò)自定義組合控件铜幽,可以對(duì)一些常用的布局以及邏輯代碼進(jìn)行封裝,以減少使用時(shí)代碼量串稀,使得代碼更加簡(jiǎn)潔除抛。
源碼
未經(jīng)本人允許禁止轉(zhuǎn)載,違者必究
簡(jiǎn)書(shū):www.reibang.com/u/3d2770e6e489
歡迎加入QQ交流群657206000點(diǎn)我加入 |
---|