心得感悟
臨近假期終于又有時(shí)間好好寫簡(jiǎn)書了,花了很久時(shí)間查閱資料,其實(shí)每次查完資料苫纤,我都感覺懂了很多,又還有好多不懂纲缓。想用三種方式做出一樣的效果卷拘,可試了很多次發(fā)現(xiàn)自己的知識(shí)儲(chǔ)備實(shí)在是嚴(yán)重不足無法實(shí)現(xiàn),唉祝高,今天也要加油鴨栗弟。
內(nèi)容簡(jiǎn)概
一、傳統(tǒng)方式(不用自定義控件)
二工闺、組合?式
三乍赫、組合?式+自定義屬性
四、效果圖
具體內(nèi)容
一陆蟆、傳統(tǒng)方式(不用自定義控件)
1. 用到的基本知識(shí)點(diǎn)
方法名 | 作用 |
---|---|
getChildAt(int position) | 獲取當(dāng)前點(diǎn)擊或者選中的View |
LayoutParams | 動(dòng)態(tài)控制子view的擺放位置 |
Gravity | 控制元素在該控件里的顯示位置 |
density | 獲取屏幕密度 |
??????????????補(bǔ)充說明 |
---|
ViewGroup.LayoutParams.MATCH_PARENT 雷厂,意思為寬度和父view相同 |
ViewGroup.LayoutParams.WRAP_CONTENT,意思為自適應(yīng) |
Gravity方法的使用與注意點(diǎn) |
屏幕密度(Density)和分辨率概念詳解 |
2. activity_main.xml
首先叠殷,我們?cè)谠撐募刑砑泳€性布局改鲫。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!--添加線性布局-->
<LinearLayout
android:id="@+id/ll_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerInParent="true">
</LinearLayout>
</RelativeLayout>
3. dot_gray_shape.xml和dot_red_shape.xml
在Android\app\res\drawable
中新建兩個(gè)drawable resource file
,并取名如標(biāo)題(當(dāng)然你也可以自己另外命名)
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!--normal狀態(tài)下-->
<solid android:color="#666666"/>
<size android:width="20dp" android:height="20dp"/>
</shape>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!--selected狀態(tài)下-->
<solid android:color="#ff0000"/>
<size android:width="30dp" android:height="30dp"/>
</shape>
4. MainActivity.java
然后通過代碼添加圓點(diǎn)控件溪猿。用代碼添加控件的好處在于簡(jiǎn)便
钩杰,這個(gè)例子中,我們需要添加5個(gè)圓點(diǎn)诊县,如果在xml中配置讲弄,會(huì)有許多重復(fù)語句;再者依痊,xml中往往是配置靜態(tài)的避除、不怎么需要變化的東西
怎披,這個(gè)例子中的圓點(diǎn)需要在手指翻閱時(shí)做一點(diǎn)動(dòng)畫。
public class MainActivity extends AppCompatActivity {
private int numberOfPages = 5;
private int currentPage = 0;
LinearLayout container;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 獲取xml配置中的線性布局容器
container = findViewById(R.id.ll_container);
// 在容器中添加內(nèi)容-View
for (int i = 0; i < 5; i++) {
// 創(chuàng)建視圖控件
ImageView dotView = new ImageView(this);
// 配置顯示樣子
if (i == 0){
// 第一個(gè)顯示紅點(diǎn)
dotView.setBackgroundResource(R.drawable.dot_red_shape);
}else {
dotView.setBackgroundResource(R.drawable.dot_gray_shape);
}
// 給控件添加左間距
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams
(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
// 設(shè)置垂直居中
params.gravity = Gravity.CENTER_VERTICAL;
// 第二個(gè)開始才需要間距
if (i > 0){
params.leftMargin = dpToPixel(10);
}
// 添加到容器中
container.addView(dotView,params);
}
}
// 由于手機(jī)屏幕密度不同瓶摆,常需要寫一些方法解決控件的顯示問題
private int dpToPixel(int dp){
// 獲取屏幕密度
float density = getResources().getDisplayMetrics().density;
return (int) (density * dp);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN){
//還原原來的
//找到page對(duì)應(yīng)的控件
ImageView dotView = (ImageView) container.getChildAt(currentPage);
dotView.setBackgroundResource(R.drawable.dot_gray_shape);
// 切換指示器
if (currentPage < numberOfPages-1){
currentPage++;
}else {
currentPage = 0;
}
// 找到當(dāng)前指示的控件
ImageView current = (ImageView) container.getChildAt(currentPage);
current.setBackgroundResource(R.drawable.dot_red_shape);
}
return true;
}
}
二凉逛、組合?式
1. PageController.java
首先需要為自定義控件創(chuàng)建一個(gè)類。在Android\(model名)\java\com.example.group文件夾中新建一個(gè)類群井,并使其繼承于LinearLayout状飞,重寫構(gòu)造方法。
2. dot_gray_shape.xml和dot_red_shape.xml
這里同樣需要設(shè)置圓點(diǎn)的樣式书斜。
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!--normal狀態(tài)下-->
<solid android:color="#666666"/>
<size android:width="20dp" android:height="20dp"/>
</shape>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!--selected狀態(tài)下-->
<solid android:color="#ff0000"/>
<size android:width="30dp" android:height="30dp"/>
</shape>
3. activity_main.xml
xml文件中只需將ConstraintLayout改為RelativeLayout诬辈,并加上ID。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:id="@+id/root">
</RelativeLayout>
4. PageController
自定義控件一般都要實(shí)現(xiàn)下圖中藍(lán)色選中的方法荐吉,否則自定義控件不起效果焙糟。
在類中設(shè)置我們需要的方法。
public class PageController extends LinearLayout {
private int numberOfPages; // 記錄多少個(gè)
private int padding; // 間距
public int normalResourse; // 正常狀態(tài)的資源
public int selectedResourse; // 選中狀態(tài)的資源
// 當(dāng)使用Java代碼創(chuàng)建控件時(shí) 用這個(gè)構(gòu)造方法
public PageController(Context context,int normalRes,int selectedRes,int padding) {
super(context,null);
// 保存外部傳過來的數(shù)據(jù)
normalResourse = normalRes;
selectedResourse = selectedRes;
this.padding = padding;
}
// xml里面配置
public PageController(Context context, @Nullable AttributeSet attrs) {
this(context,attrs,0);
}
// xml里面還配置了樣式的
public PageController(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setOrientation(LinearLayout.HORIZONTAL);
}
/**
* setter/getter方法
*/
public int getNumberOfPages() {
return numberOfPages;
}
public void setNumberOfPages(int numberOfPages) {
this.numberOfPages = numberOfPages;
// 依次創(chuàng)建每個(gè)指示點(diǎn)
for (int i = 0; i < numberOfPages; i++) {
// 創(chuàng)建控件
ImageView dotView = new ImageView(getContext());
// 創(chuàng)建布局參數(shù)
LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
// 設(shè)置資源
if (i == 0){
dotView.setImageResource(selectedResourse);
}else {
dotView.setImageResource(normalResourse);
// 設(shè)置間距
params.leftMargin = padding;
}
// 垂直居中
params.gravity = Gravity.CENTER_VERTICAL;
// 添加控件
addView(dotView,params);
}
}
/**
* padding的setter/getter方法
*/
public int getPadding() {
return padding;
}
public void setPadding(int padding) {
this.padding = padding;
}
/**
* 定義一個(gè)接口 在接口里面定義常量表示狀態(tài)
*/
public interface DotState{
int NORMAL = 0;
int SELECTED = 1;
}
}
對(duì)比傳統(tǒng)方式样屠,組合方式創(chuàng)建自定義控件可以使xml文件和Java文件代碼更加簡(jiǎn)潔
穿撮。
三、組合?式+自定義屬性
1. PageController.java
首先痪欲,和方式二相同創(chuàng)建一個(gè)類繼承于LinearLayout悦穿,并重寫構(gòu)造方法。
public class PageController extends LinearLayout {
private int numberOfPages;
public int resourceID; // 不同狀態(tài)下顯示的形狀和顏色
public int padding; // 間距
public int currentPage; // 記錄當(dāng)前指示是第幾個(gè)
private PageChangeListener mPageChangeListener; // 回調(diào)對(duì)象
// 代碼創(chuàng)建
public PageController(Context context) {
this(context,null);
}
// xml配置
public PageController(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public PageController(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 設(shè)置對(duì)齊方式
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER);
/**
* 將xml里面自定義的屬性取出來
*/
if (attrs != null){
/**
* 從一個(gè)資源文件里面將自定義的所有屬性取出來
* 1. Attributes xml配置里的所有屬性
* 2. 自定義的屬性文件 R.styleable.name
*/
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PageController);
/**
* 1. 自定義屬性名 自定義屬性名_屬性名
* 2. 默認(rèn)值
*/
padding = typedArray.getInt(R.styleable.PageController_mPadding,0);
resourceID = typedArray.getResourceId(R.styleable.PageController_resourceID,0);
int page = typedArray.getInt(R.styleable.PageController_numberOfPage,0);
// 顯示
setNumberOfPages(page);
}
}
/**
* numberOfPages setter/getter方法
*/
public int getNumberOfPages() {
return numberOfPages;
}
public void setNumberOfPages(int numberOfPages) {
this.numberOfPages = numberOfPages;
// 創(chuàng)建點(diǎn)
for (int i = 0; i < numberOfPages; i++) {
// 創(chuàng)建控件
ImageView dotView = new ImageView(getContext());
// 設(shè)置顯示的內(nèi)容
dotView.setBackgroundResource(resourceID);
// 設(shè)置約束
LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_VERTICAL;
if (i > 0) {
params.leftMargin = padding;
}else {
// 默認(rèn)選擇第一個(gè)點(diǎn)
dotView.setEnabled(false);
}
// 添加到容器中
addView(dotView,params);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN){
// 取出當(dāng)前頁數(shù)
int current = currentPage;
if (event.getX() > getWidth()*0.5){
// 右邊
if (current == numberOfPages - 1){
current = 0;
}else {
current++;
}
}else {
// 左邊
if (current == 0){
current = numberOfPages - 1;
}else {
current--;
}
}
setCurrentPage(current);
}
return true;
}
/**
* currentPage
*/
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
// 將上一次的還原為默認(rèn)狀態(tài)
ImageView old = (ImageView) getChildAt(this.currentPage);
old.setEnabled(true);
// 將當(dāng)前選中的設(shè)置為選中狀態(tài)
ImageView current = (ImageView) getChildAt(currentPage);
current.setEnabled(false);
this.currentPage = currentPage;
// 將頁數(shù)改變的事件回調(diào)給監(jiān)聽者
if (mPageChangeListener != null){
mPageChangeListener.pageDidChange(currentPage);
}
//開啟動(dòng)畫
showAnimation(current);
}
/**
* 定義接口監(jiān)聽指示器改變的事件
*/
public interface PageChangeListener{
void pageDidChange(int currentPage);
}
/**
* mPageChangeListener
* 設(shè)置監(jiān)聽對(duì)象
*/
public void addPageChangeListener(PageChangeListener listener){
this.mPageChangeListener = listener;
}
/**
* 寬度拉伸的動(dòng)畫
*/
public void showAnimation(ImageView item){
ObjectAnimator scale = ObjectAnimator.ofFloat(item,"scaleX",1,1.5f,1);
scale.setDuration(400);
scale.start();
}
/**
* padding
*/
public int getPadding() {
return padding;
}
public void setPadding(int padding) {
this.padding = padding;
}
}
2. page_control_attr.xml
在customattr/res/values中新建一個(gè)Value resource file业踢,編寫自定義屬性咧党。
創(chuàng)建自定義屬性說明:
①在Value中新建一個(gè)Value resource file
②使用declare-styleable關(guān)鍵字修飾
③name為自己定義的類名
④format添加屬性和對(duì)應(yīng)的值的類型
⑤可以添加多種類型,類型間用“ | ”隔開
<resources>
<!--聲明在哪個(gè)控件上添加屬性-->
<declare-styleable name="PageController">
<attr name="mPadding" format="integer"/>
<attr name="resourceID" format="reference|color"/>
<attr name="numberOfPage" format="integer"/>
</declare-styleable>
</resources>
3 dot_shape.xml
在drawable文件下新建一個(gè)資源文件陨亡,設(shè)置圓點(diǎn)形狀。
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--enables為false 選中-->
<item android:state_enabled="false">
<shape android:shape="oval">
<size android:height="50dp" android:width="50dp"></size>
<solid android:color="#ff0000"></solid>
</shape>
</item>
<!--enables為true 默認(rèn)-->
<item>
<shape android:shape="oval">
<size android:height="50dp" android:width="50dp"></size>
<solid android:color="#666666"></solid>
</shape>
</item>
</selector>
4. activity_main.xml
添加自定義布局屬性深员。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:id="@+id/root">
<com.example.customattr.PageController
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:mPadding="20"
app:resourceID="@drawable/dot_shape"
app:numberOfPage="5"/>
</RelativeLayout>
5. MainActivity.java
很明顯代碼又簡(jiǎn)潔了許多负蠕。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
四、效果圖
-
組合方式
-
繼承方式(靜態(tài))
-
自繪方式