心得感悟
临近假期终于又有时间好好写简书了,花了很久时间查阅资料,其实每次查完资料,我都感觉懂了很多,又还有好多不懂。想用三种方式做出一样的效果,可试了很多次发现自己的知识储备实在是严重不足无法实现,唉,今天也要加油鸭。
内容简概
一、传统方式(不用自定义控件)
二、组合⽅式
三、组合⽅式+自定义属性
四、效果图
具体内容
一、传统方式(不用自定义控件)
1. 用到的基本知识点
方法名 | 作用 |
---|---|
getChildAt(int position) | 获取当前点击或者选中的View |
LayoutParams | 动态控制子view的摆放位置 |
Gravity | 控制元素在该控件里的显示位置 |
density | 获取屏幕密度 |
补充说明 |
---|
ViewGroup.LayoutParams.MATCH_PARENT ,意思为宽度和父view相同 |
ViewGroup.LayoutParams.WRAP_CONTENT,意思为自适应 |
Gravity方法的使用与注意点 |
屏幕密度(Density)和分辨率概念详解 |
2. activity_main.xml
首先,我们在该文件中添加线性布局。
<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
中新建两个drawable resource file
,并取名如标题(当然你也可以自己另外命名)
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!--normal状态下-->
<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状态下-->
<solid android:color="#ff0000"/>
<size android:width="30dp" android:height="30dp"/>
</shape>
4. MainActivity.java
然后通过代码添加圆点控件。用代码添加控件的好处在于简便
,这个例子中,我们需要添加5个圆点,如果在xml中配置,会有许多重复语句;再者,xml中往往是配置静态的、不怎么需要变化的东西
,这个例子中的圆点需要在手指翻阅时做一点动画。
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);
// 在容器中添加内容-View
for (int i = 0; i < 5; i++) {
// 创建视图控件
ImageView dotView = new ImageView(this);
// 配置显示样子
if (i == 0){
// 第一个显示红点
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);
// 设置垂直居中
params.gravity = Gravity.CENTER_VERTICAL;
// 第二个开始才需要间距
if (i > 0){
params.leftMargin = dpToPixel(10);
}
// 添加到容器中
container.addView(dotView,params);
}
}
// 由于手机屏幕密度不同,常需要写一些方法解决控件的显示问题
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对应的控件
ImageView dotView = (ImageView) container.getChildAt(currentPage);
dotView.setBackgroundResource(R.drawable.dot_gray_shape);
// 切换指示器
if (currentPage < numberOfPages-1){
currentPage++;
}else {
currentPage = 0;
}
// 找到当前指示的控件
ImageView current = (ImageView) container.getChildAt(currentPage);
current.setBackgroundResource(R.drawable.dot_red_shape);
}
return true;
}
}
二、组合⽅式
1. PageController.java
首先需要为自定义控件创建一个类。在Android\(model名)\java\com.example.group文件夹中新建一个类,并使其继承于LinearLayout,重写构造方法。
2. dot_gray_shape.xml和dot_red_shape.xml
这里同样需要设置圆点的样式。
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!--normal状态下-->
<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状态下-->
<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
自定义控件一般都要实现下图中蓝色选中的方法,否则自定义控件不起效果。
在类中设置我们需要的方法。
public class PageController extends LinearLayout {
private int numberOfPages; // 记录多少个
private int padding; // 间距
public int normalResourse; // 正常状态的资源
public int selectedResourse; // 选中状态的资源
// 当使用Java代码创建控件时 用这个构造方法
public PageController(Context context,int normalRes,int selectedRes,int padding) {
super(context,null);
// 保存外部传过来的数据
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;
// 依次创建每个指示点
for (int i = 0; i < numberOfPages; i++) {
// 创建控件
ImageView dotView = new ImageView(getContext());
// 创建布局参数
LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
// 设置资源
if (i == 0){
dotView.setImageResource(selectedResourse);
}else {
dotView.setImageResource(normalResourse);
// 设置间距
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;
}
/**
* 定义一个接口 在接口里面定义常量表示状态
*/
public interface DotState{
int NORMAL = 0;
int SELECTED = 1;
}
}
对比传统方式,组合方式创建自定义控件可以使xml文件和Java文件代码更加简洁
。
三、组合⽅式+自定义属性
1. PageController.java
首先,和方式二相同创建一个类继承于LinearLayout,并重写构造方法。
public class PageController extends LinearLayout {
private int numberOfPages;
public int resourceID; // 不同状态下显示的形状和颜色
public int padding; // 间距
public int currentPage; // 记录当前指示是第几个
private PageChangeListener mPageChangeListener; // 回调对象
// 代码创建
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);
// 设置对齐方式
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER);
/**
* 将xml里面自定义的属性取出来
*/
if (attrs != null){
/**
* 从一个资源文件里面将自定义的所有属性取出来
* 1. Attributes xml配置里的所有属性
* 2. 自定义的属性文件 R.styleable.name
*/
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PageController);
/**
* 1. 自定义属性名 自定义属性名_属性名
* 2. 默认值
*/
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;
// 创建点
for (int i = 0; i < numberOfPages; i++) {
// 创建控件
ImageView dotView = new ImageView(getContext());
// 设置显示的内容
dotView.setBackgroundResource(resourceID);
// 设置约束
LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_VERTICAL;
if (i > 0) {
params.leftMargin = padding;
}else {
// 默认选择第一个点
dotView.setEnabled(false);
}
// 添加到容器中
addView(dotView,params);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN){
// 取出当前页数
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) {
// 将上一次的还原为默认状态
ImageView old = (ImageView) getChildAt(this.currentPage);
old.setEnabled(true);
// 将当前选中的设置为选中状态
ImageView current = (ImageView) getChildAt(currentPage);
current.setEnabled(false);
this.currentPage = currentPage;
// 将页数改变的事件回调给监听者
if (mPageChangeListener != null){
mPageChangeListener.pageDidChange(currentPage);
}
//开启动画
showAnimation(current);
}
/**
* 定义接口监听指示器改变的事件
*/
public interface PageChangeListener{
void pageDidChange(int currentPage);
}
/**
* mPageChangeListener
* 设置监听对象
*/
public void addPageChangeListener(PageChangeListener listener){
this.mPageChangeListener = listener;
}
/**
* 宽度拉伸的动画
*/
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中新建一个Value resource file,编写自定义属性。
创建自定义属性说明:
①在Value中新建一个Value resource file
②使用declare-styleable关键字修饰
③name为自己定义的类名
④format添加属性和对应的值的类型
⑤可以添加多种类型,类型间用“ | ”隔开
<resources>
<!--声明在哪个控件上添加属性-->
<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文件下新建一个资源文件,设置圆点形状。
<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 默认-->
<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
很明显代码又简洁了许多。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
四、效果图
-
组合方式
-
继承方式(静态)
-
自绘方式