默认的Toast:

1
2
3

Toast.makeText(ProjectList.this,"数据请求失败",Toast.LENGTH_LONG).show();

image

自定义Toast(自定义图标样式\显示时间\位置):

效果图:

image

代码:

1
2
3
4
5
6
7

MyUtils.showToast(TestActivity.this,"自定义的Toast");

或者 传入自定义显示时间

MyUtils.showToast(TestActivity.this,"自定义的Toast",3000);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

public class MyUtils {

private static Toasttoast;

private static Handlermhandler =new Handler();

private static Runnable r =new Runnable() {

public void run() {

isShowing=false;

        toast.cancel();

    }

};

private static BooleanisShowing=false;

public static void showToast(final Context context, final String msg){

if (!isShowing){

if (toast ==null)

toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);

        toast.setGravity(Gravity.CENTER, 0, 0);

        Activity context1 = (Activity) context;

        LayoutInflater layoutInflater = context1.getLayoutInflater();

        View inflate = layoutInflater.inflate(R.layout.toast_layout, null);

        TextView toast_msg = inflate.findViewById(R.id.toast_msg);

        toast_msg.setText(msg);

        toast.setView(inflate);

        toast.show();

        isShowing=true;

        mhandler.postDelayed(r, 2000);

    }

}

public static void showToast(final Context context, final String msg,int time){

if (!isShowing){

if (toast ==null)

toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);

        toast.setGravity(Gravity.CENTER, 0, 0);

        Activity context1 = (Activity) context;

        LayoutInflater layoutInflater = context1.getLayoutInflater();

        View inflate = layoutInflater.inflate(R.layout.toast_layout, null);

        TextView toast_msg = inflate.findViewById(R.id.toast_msg);

        toast_msg.setText(msg);

        toast.setView(inflate);

        toast.show();

        isShowing=true;

        mhandler.postDelayed(r, time);

    }

}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    xmlns:app="http://schemas.android.com/apk/res-auto">

            android:layout_width="match_parent"

            android:layout_height="match_parent">

                    android:padding="8dp"

                    android:orientation="vertical"

                    android:layout_centerInParent="true"

                    android:background="@drawable/toast_shape"

                    android:layout_width="wrap_content"

                    android:minWidth="80dp"

                    android:layout_height="wrap_content">

                            android:layout_marginTop="4dp"

                            android:layout_gravity="center"

                            android:src="@mipmap/tishi"

                            android:layout_width="wrap_content"

                            android:layout_height="wrap_content" />

                            android:id="@+id/toast_msg"

                            android:layout_marginTop="6dp"

                            android:textSize="14dp"

                            android:textColor="@color/white"

                            android:layout_gravity="center"

                            android:layout_width="wrap_content"

                            android:layout_height="wrap_content" />

</RelativeLayout>

android6.0:全称是Marshmallow(棉花糖)
goole在2015年I/O大会上正式公布
#####主要更新模块:

  • 电源管理:
    App Standby(应用待机状态,以前只有硬件待机);
    检测:当设备部充电,且在这时间呢用户没有直接或间接的启动该应用;
    退出:当应用被激活时,或者设备充电时,系统将应用移除此状态;

  • Doze:
    检测:当设备不充电,且当设备静止且灭屏一段时间;
    周期:平台尝试让系统处于休眠状态,周期性的进入在一个维持窗口恢复正常操作,然后进入更惨的休眠状态;

  • App Link:
    全称为应用程序链接;
    技术点:就是我们的隐示启动Intent;
    android更加孤立应用程序见的关联而不再试单一的应用同浏览器的交互;

  • 指纹识别:
    6.0以前一直由各手机制造厂商去研发,导致指纹识别不同的手机差异巨大;
    6.0以后由android系统提供API,硬件厂商只需提供相应的硬件支持即可;

  • 应用权限管理(最重要):
    5.0以前,只需要在manifest.xml中注册声明即可;
    5.0以后,用户可以在安装的时候关闭某些权限;
    6.0以后,对于一些用户隐私权限总数会在第一次提示用户是否授予权限(和IPONE类似)

  • 运行时权限的优点:
    新的权限机制更好的保护了用户的隐私;
    给了程序向用户说明权限的作用;
    可以防止一些而已程序盗取用户或者手机信息,增强了android系统的安全性;

  • ###为什么要使用异步任务
  1. android单线程模型
  2. 耗时操作放在非主线程执行
  • ###AsyncTask为何而生
  1. 子线程中更新ui
  2. 封装、简化异步操作
  • ###构建AsyncTask子类的参数

AsyncTask<Params,Progress,Result>是一个抽象类。
通常用于被继承,继承AsyncTask需要指定如下三个泛型参数:

  • Params:启动任务时输入参数的类型。

  • Progress:后台任务执行中返回进度值的类型。

  • Result: 后台执行任务完成后返回结果的类型。

  • ###构建AsyncTask子类的回调方法

  • doInBackground:必须重写,异步执行后台线程将要完成的任务

  • onPreExecute:执行后台耗时操作前被调用,通常用户完成一些初始化操作

  • onPostExcute:当doInBackground()完成后,系统会自动调用 onPostExcute(),并将doInBackground方法返回的值传给该方法

  • onProgressUpdate: 在doInBackground方法中调用publishProgress()方法更新任务的执行进度后,就会调用该方法。

  • ###AsyncTask注意事项

  • 必须在ui线程中创建AsyncTask的实例

  • 必须在ui线程中调用AsyncTask的execute()方法。

  • 重写的四个方法是系统自带调用的,不应受到调用

  • 每个AsyncTsk只能被执行一次,多次调用将会引发异常

  • ###AsyncTask模拟网络图片加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
113
package com.tyl.mystudy.asynctask;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import androidx.annotation.Nullable;
import com.tyl.mystudy.R;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
/**
* Author tyl
* Created on 2020/2/6 12:52
* Describe:模拟网络图片加载
*/
public class AsyncTaskActivity extends Activity {
private ImageView iv_image;
private ProgressBar pro_progress;
private String imgUrl="https://www.imooc.com/static/img/index/logo.png";
private MyAsyncTask myAsyncTask;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_asynctask);
iv_image=findViewById(R.id.iv_image);
pro_progress=findViewById(R.id.pro_progress);
myAsyncTask = new MyAsyncTask();
myAsyncTask.execute(imgUrl);//将url作为参数传入
}
@Override
protected void onDestroy() {
super.onDestroy();
if (myAsyncTask!=null&&myAsyncTask.getStatus()== AsyncTask.Status.RUNNING){
//asyncTask还在运行
//cancel方法只是将对应的asyncTask标记为cancel状态并不是真正的取消线程的执行
// 可以在doInBackground方法中通过isCancelled()方法判断是否已处于cancel如果true则手动结束后台任务
myAsyncTask.cancel(true);
}
}
public class MyAsyncTask extends AsyncTask<String,Integer, Bitmap> {
/**
* AsyncTask<Params,Progress,Result>是一个抽象类
* Params:启动任务时输入参数的类型。
* Progress:后台任务执行中返回进度值的类型
* Result: 后台执行任务完成后返回结果的类型。
*
* 调用该AsyncTask方法:
* MyAsyncTask myAsyncTask = new MyAsyncTask();
* myAsyncTask.execute();
* 调用后的代码执行顺序:
*onPreExecute()-doInBackground()-onPostExecute()
* onProgressUpdate()需要在doInBackground方法中调用publishProgress()方法更新任务的执行进度后,才会调用该方法。
* */
@Override
protected Bitmap doInBackground(String... params) {
//可以在这里执行一些异步任务 这里的所以执行都在子线程中
// publishProgress();//可以传入值,并调用onProgressUpdate()
if (!isCancelled()){
String url=params[0];
Bitmap bitmap=null;
URLConnection connection;
InputStream inputStream;
try {
connection=new URL(url).openConnection();//获取网络连接对象
inputStream=connection.getInputStream();//获取到输入流
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
bitmap = BitmapFactory.decodeStream(bufferedInputStream);//将输入流解析成bitMap
inputStream.close();//关闭输入流
bufferedInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
//将bitmap作为返回值返回
return bitmap;
}
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
//可以在这里执行一些代码初始化的工作
iv_image.setVisibility(View.GONE);
pro_progress.setVisibility(View.VISIBLE);
}

@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
//在这里与ui线程通信 这个方法运行在主线程
if (bitmap!=null){
iv_image.setImageBitmap(bitmap);
iv_image.setVisibility(View.VISIBLE);
pro_progress.setVisibility(View.GONE);
}
}

@Override
protected void onProgressUpdate(Integer... integers) {
super.onProgressUpdate(integers);
//在这里执行进度更新
// pro_progress.setProgress(integers[0]);
}

}
}

####CountDownTimer:
官方文档。CountDownTimer是一个倒计时的类,还可以指定时间间隔定期通知,举个栗子,比如说你倒计时是100秒的,可以指定每20秒通知一次,这样开始的时候会回调一次,20秒时会回调一次,40秒时会回调一次…,200秒时的回调和时间间隔的回调不同方法的。
常用方法:

  • cancel() 取消倒计时
  • onFinish() 时间到了,就会触发回调。
  • onTick 定期间隔触发回调
  • start() 开始倒计时

#####示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
//new CountDownTimer(倒计时多久, 倒计时速度) 毫秒为单位
CountDownTimer timer = new CountDownTimer(10000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
mTimer2.setEnabled(false);
mTimer2.setText("已发送(" + millisUntilFinished / 1000 + ")");
}
@Override
public void onFinish() {
mTimer2.setEnabled(true);
mTimer2.setText("重新获取验证码");
}
}.start();

在android开发中我们很多地方都用到了方法的回调,回调就是把方法的定义和功能导入实现分开的一种机制,目的是为了解耦他的本质是基于观察者设计模式,即观察者设计模式的的简化版,例如:在下载时候的进度回调,在adapter与activity之间的回调,在javabean和fragment以及fragment之间的回调等等,回调的目的主要有两个:其一是传递数据,其二是保持数据的同步更新。常用的有两种形式,一是使用内部类的形式,得到接口的子类对象,另一种是直接实现定义的接口。

####一、内部类的形式

1、在需要传递数据的一端定义一个接口,接口里面些需要监听的方法以及参数。

2、定义一个的接口类型的变量存储数据。

3、创建一个公共的方法,让外部调用,并且传递接口类型的参数,给其定义的接口类型的数据初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 定义一个接口
*/
public interface onListener{
void OnListener(String code,String msg);
}
/**
*定义一个变量储存数据
*/
private onListener listener;
/**
*提供公共的方法,并且初始化接口类型的数据
*/
public void setListener( onListener listener){
this.listener = listener;
}

4、在合适的位置调用接口里面的方法,传递数据。

1
2
3
4
5
6
/**
* 在合适的位置给其调用接口,给其赋值
*/
if (listener != null) {
listener.OnListener(rtncode, rtnmsg);
}

5、在需要获取数据的地方,创建对象调方法

1
2
3
4
5
6
7
Print print = new Print();
print.setListener(new PrintTicket.onListener() {
@Override
public void OnListener(String code, String msg) {
//在这里获取数据进行处理
}
});

二、实现接口的形式
1、定义一个接口,可以另起包名,或者定义在类里面。

1
2
3
4
5
6
/**
* 定义一个接口
*/
public interface onListener{
void OnListener(String code,String msg);
}

2、在需要传递数据的一端的构造方法对接口进行初始化。

1
2
3
public Print(OnListener listener) {
this.listener = listener;
}

3、在合适的位置调用接口里面的方法,传递数据。

1
2
3
4
5
6
/**
* 在合适的位置给其调用接口,给其赋值
*/
if (listener!=null) {
listener.OnListener(rtncode,rtnmsg);
}

4、在需要获取数据的地方创建对象传递参数。

1
Print print = new Print(this);

这里面的this代指的是当前页面的activity,如果是fragment的话,需要重写onAttach方法对其进行初始化,强制类型转换后获取接口对象。此时在构造方法里面传递接口得数据listener,如下二选一。

1
2
3
4
5
6
7
8
9
10
11
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
OnListener listener = (OnListener) activity;
}

@Override
public void onAttach(Context context) {
OnListener listener = (OnListener) activity;
}

注意:直接在fragment里面进行获取当前fragment所在的activity,调用getActivity进行强转或者直接传递会报错。报的错误为类型转换错误。
5、让activity实现此接口,重写其抽象方法,在抽象法里面处理任务。

1
2
3
4
@Override
public void OnListener(String rtncode, String rtnmsg) {
//在这里获取数据进行处理
}

以上两个回调方法适用于两个类之间的数据传递,现在来看看三个类之间的数据传递,javabean——>activity——>fragment之间的数据传递。需求:在javabean中获取数据,当点击fragment按钮时候,要求显示获取的数据进行相关业务处理。步骤如下:
1、新建一个包,定义一个接口,定义相关方法。

2、在javabean的构造方法中对接口进行初始化,并在适当的位置调用一下接口中的方法。

3、在fragment中重写onAttach,对接口进行初始化,强转为接口类型。

4、创建对象,传递参数为3中强转的接口类型。

5、让acitivity实现接口,重写抽象方法,在方法中进行数据的处理。

具体代码就省略了……
具体应用见:http://blog.csdn.net/yoonerloop/article/details/52127143

  • ContextCompact.checkSelfPermission 检查是否已有某个权限
  • ActivityCompat.requestPermissions() 权限请求
  • onRequestPermissionResult() 权限请求后的回调
  • ActivityCompat.shouldShowRequestPermissionRationale 可以给用户展示的一个权限请求来的作用,在权限被用户拒绝的情况才会出现

#####危险权限:

  • CALENDAR
  • CAMERA
  • CONTACTS
  • LOCATION
  • MICROPHONE
  • PHONE
  • SENSORS
  • SMS
  • STORAGE

#####详情:
详情
#####简单的权限请求demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.bn_main).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
callPhone();//模拟打电话
}
});
}
private void callPhone(){
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)!= PackageManager.PERMISSION_GRANTED){
//权限请求未被同意
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CALL_PHONE},1);
}else {
//权限请求被同意
}
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case 1://请求的权限码
if (grantResults[0]==PackageManager.PERMISSION_GRANTED){
//权限被同意
}else {
//权限被拒绝 提示用戶沒有權限

}
break;
}
}
}

#####封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 檢查是否由權限
* */
private Boolean hasPermission(String ... permissions){
for(String permission:permissions){
if (ContextCompat.checkSelfPermission(this,permission)!= PackageManager.PERMISSION_GRANTED){
return false;
}
}
return true;
}
/**
* 權限請求
* */
private void requestPermissions(int code,String ... permissions){
ActivityCompat.requestPermissions(this,permissions,code);
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

switch (requestCode){
case Constants.CALL_CODE:

break;
}
}

什么是ANR:
Application Not Responding;
应用程序无响应弹出的对话框;
应用程序的响应是由Activity Manager和Window Manager监视的;
一般activity5秒无响应或者notifcation10秒没反应或者没有响应结束就会弹出ANR;

造成ANR的主要原因:
主线程被阻塞(4.0以后不允许在主线程中执行网络IO操作);
主线程中存在耗时操作;

如何避免ANR:
使用AsyncTask处理耗时操作;
使用Thread和HandlerThread提高优先级;
使用Handler来处理工作线程的耗时操作;
Activity的onCreate和onReseme回调中尽量避免耗时代码;

######android:scaleType=“center”

  • 保持原图的大小,显示在ImageView的中心。当原图的size大于ImageView的size时,多出来的部分被截掉。

######android:scaleType=“center_inside”

  • 以原图正常显示为目的,如果原图大小大于ImageView的size,就按照比例缩小原图的宽高,居中显示在ImageView中。如
    果原图size小于 ImageView的size,则不做处理居中显示图片。

######android:scaleType=“center_crop”

  • 以原图填满ImageView为目的,如果原图size大于ImageView的size,则与center_inside一样,按比例缩小,居中显示在ImageView上。如果原图size小于ImageView的size,则按比例拉升原图的宽和高,填充ImageView居中显示。
android:scaleType=“matrix”
  • 不改变原图的大小,从ImageView的左上角开始绘制,超出部分做剪切处理。

######androd:scaleType=“fit_xy”

  • 把图片按照指定的大小在ImageView中显示,拉伸显示图片,不保持原比例,填满ImageView.

######android:scaleType=“fit_start”

  • 把原图按照比例放大缩小到ImageView的高度,显示在ImageView的start(前部/上部)。

######android:sacleType=“fit_center”

  • 把原图按照比例放大缩小到ImageView的高度,显示在ImageView的center(中部/居中显示)。

######android:scaleType=“fit_end”

  • 把原图按照比例放大缩小到ImageView的高度,显示在ImageVIew的end(后部/尾部/底部)
    image.png

ConstraintLayout(约束布局)已经推出有一段时间了,在 Android Studio 中也作为了默认布局,能够减少布局的层级并改善布局性能,因此很有必要来研究下其功能与使用方法
ConstraintLayout 能够灵活地定位和调整子View的大小,子 View 依靠约束关系来确定位置。在一个约束关系中,需要有一个 Source(源)以及一个 Target(目标),Source 的位置依赖于 Target,可以理解为“通过约束关系,Source 与 Target链接在了一起,Source 相对于 Target 的位置便是固定的了
#####一、基本属性
ConstraintLayout 最基本的属性控制有以下几个,即 layout_constraintXXX_toYYYOf 格式的属性,即将“View A”的方向 XXX 置于 “View B”的方向 YYY 。当中,View B 可以是父容器即 ConstraintLayout ,用“parent”来表示

  • layout_constraintBaseline_toBaselineOf (View A 内部文字与 View B 内部文字对齐)
  • layout_constraintLeft_toLeftOf (View A 与 View B 左对齐)
  • layout_constraintLeft_toRightOf (View A 的左边置于 View B 的右边)
  • layout_constraintRight_toLeftOf (View A 的右边置于 View B 的左边)
  • layout_constraintRight_toRightOf
  • layout_constraintTop_toTopOf
  • layout_constraintTop_toBottomOf
  • layout_constraintBottom_toTopOf
  • layout_constraintBottom_toBottomOf
  • layout_constraintStart_toEndOf
  • layout_constraintStart_toStartOf
  • layout_constraintEnd_toStartOf
  • layout_constraintEnd_toEndOf
    各个方位的参照图可以看以下图片
    image.png
    ConstraintLayout 的这些属性是为控件添加了某个方向的约束力,根据某个方向约束力的“有无”或“强弱”,控件会位于不同的位置
    例如,看以下代码,根据某个方向的约束的不同,控件会居中或者不会
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
    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="com.czy.constraintlayoutdemo.MainActivity">

    <TextView
    android:id="@+id/tv1"
    android:layout_width="200dp"
    android:layout_height="150dp"
    android:layout_margin="20dp"
    android:background="#f5ec7e"
    android:gravity="center"
    android:text="Hello World!"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@+id/tv2"
    app:layout_constraintTop_toTopOf="parent"/>

    <TextView
    android:id="@+id/tv2"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="20dp"
    android:background="#68b0f9"
    android:gravity="center"
    android:text="没有设置底部约束,所以不会居于中间"
    app:layout_constraintLeft_toRightOf="@+id/tv1"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="@+id/tv1"/>

    <TextView
    android:id="@+id/tv3"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="20dp"
    android:background="#984ff7"
    android:gravity="center"
    android:text="上下均设置了约束,所以会居于中间"
    app:layout_constraintBottom_toBottomOf="@+id/tv1"
    app:layout_constraintLeft_toRightOf="@+id/tv1"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/tv2"/>

    </android.support.constraint.ConstraintLayout>
    image.png
    可以看到,蓝色方块由于只设置了顶部约束而没有设置底部约束,所有蓝色方块只会与黄色方块顶部对齐,而紫色方块由于上下均设置了约束,且约束力的“强度”相同,所以紫色方块会呈现居中效果

#####二、约束力的强度
那么,约束力的“强度”该如何设定呢?可以依靠 layout_constraintHorizontal_bias 和 layout_constraintVertical_bias 两个属性,即用来设置控件在水平和垂直方向的偏移量
例如,以下布局中, TextView 应该是处于水平和垂直两个方向居中的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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="com.czy.constraintlayoutdemo.MainActivity">

<TextView
android:id="@+id/tv1"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#f5ec7e"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>

image.png
而在以下代码中,黄色方块左侧所占的剩余空间从50%变成了100%,上侧所占的剩余空间从50%变成了10%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="com.czy.constraintlayoutdemo.MainActivity">

<TextView
android:id="@+id/tv1"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#f5ec7e"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1" />

</android.support.constraint.ConstraintLayout>

image.png
#####三、Visibility 属性
在以前的布局中,如果 View 的 visibility 属性设置为 gone,那么其他原本依赖该 View 来参照定位的属性都会失效,而在 ConstraintLayout 布局中会有所不同
例如,在以下布局中,红色方块位于屏幕右上角与黄色方块左下角形成的矩形的中间位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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="com.czy.constraintlayoutdemo.MainActivity">

<TextView
android:id="@+id/tv1"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#f5ec7e"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

<TextView
android:id="@+id/tv2"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#fa6e61"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="@+id/tv1"
app:layout_constraintLeft_toLeftOf="@+id/tv1"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>

image.png
如果将黄色方块的 visibility 属性设置为 gone,那么,红色方块的位置会发生变化。可以理解为黄色方块缩小为一个不可见的小点,位于其原先位置的中间,而红色方块则改为依照该点来进行定位
image.png
此时,红色方块可以依靠 layout_goneMarginBottom 、layout_goneMarginStart等属性来设置与黄色方块之间的边距,这类属性只有在黄色方块的 visibility 属性设置为gone时才会生效
#####四、宽高比
在其他布局中,如果想根据屏幕的宽度来为 View 设置固定的宽高比的话是比较麻烦的,但在 ConstraintLayout 中可以直接设置
例如,如果想实现一个固定宽高比的顶部标题栏的话,可以将宽和高设置为 0dp,然后为其设置 app:layout_constraintDimensionRatio 属性,设定宽高比为16:7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="com.czy.constraintlayoutdemo.MainActivity">

<TextView
android:id="@+id/tv1"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#f5ec7e"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintDimensionRatio="h,16:7"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/tv2"
android:layout_width="100dp"
android:layout_height="0dp"
android:background="#5476fd"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="W,15:25"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv1" />

</android.support.constraint.ConstraintLayout>

image.png
要使用 layout_constraintDimensionRatio 属性,需要至少设置宽度或者高度为 0dp,宽高的尺寸比例可以通过“float值”或者“宽度:高度”的形式来设置
如果宽度和高度都是 0dp ,系统会使用满足所有约束条件和宽高比率值的最大尺寸
如果要根据其中一个尺寸来约束另外一个尺寸,则可以在比率值的前面添加 W 或者 H 来指明约束宽度或者高度
#####五、设置控件之间的宽高占比
ConstraintLayout 也可以像 LinearLayout 一样为子控件设置 layout_weight 属性,从而控件子控件之间的宽高占比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="com.czy.constraintlayoutdemo.MainActivity">

<TextView
android:id="@+id/tv1"
android:layout_width="0dp"
android:layout_height="50dp"
android:background="#f5ec7e"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintHorizontal_weight="1.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/tv2"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/tv2"
android:layout_width="0dp"
android:layout_height="50dp"
android:background="#55e4f4"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toRightOf="@+id/tv1"
app:layout_constraintRight_toLeftOf="@+id/tv3"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/tv3"
android:layout_width="0dp"
android:layout_height="50dp"
android:background="#f186ad"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toRightOf="@+id/tv2"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

#####六、锚向指示线
当需要一个任意位置的锚点时,可以使用指示线(Guideline)来帮助定位,指示线实际上是 View 的子类,使用方式和普通的 View 相同,但指示线有着如下的特殊属性:

  • 宽度和高度均为0
  • 可见性为 View.GONE

即指示线只是为了帮助其他 View 定位而存在,实际上并不会出现在实际界面中
例如,如下代码加入了两条指示线,可以选择使用百分比或实际距离来设置指示线的位置,此外也可以通过 orientation 属性来设置指示线的方向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="com.czy.constraintlayoutdemo.MainActivity">

<android.support.constraint.Guideline
android:id="@+id/guideline1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />

<android.support.constraint.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="100dp" />

<TextView
android:id="@+id/tv1"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="#f5ec7e"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintLeft_toRightOf="@+id/guideline1"
app:layout_constraintTop_toBottomOf="@+id/guideline2" />

</android.support.constraint.ConstraintLayout>

设置横向指示线距离顶部 100dp
image.png
竖向指示线设置其距离百分比为 0.5,所以是位于屏幕的中间位置
#####七、Chains链
Chain 链比较难描述,它是一种特殊的约束,可以为多个通过 chain 链连接的 View 来分发剩余空间位置,类似于 LinearLayout 中的权重比 weight 属性,但 Chains 链要强大得多
如果几个View之间通过双向连接而互相约束对方的位置,那么将其视为链条
例如,以下布局代码所呈现出来的效果,就可以称为一条链条,在这条链的最左侧的元素成为链头,可以在其身上设置一些属性以决定这个链的展示效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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="com.czy.constraintlayoutdemo.MainActivity">

<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:background="#f5ec7e"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/tv2"
app:layout_constraintTop_toTopOf="parent"/>

<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginTop="0dp"
android:background="#ff538c"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintLeft_toRightOf="@+id/tv1"
app:layout_constraintRight_toLeftOf="@+id/tv3"
app:layout_constraintTop_toTopOf="parent"/>

<TextView
android:id="@+id/tv3"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:background="#41c0ff"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintLeft_toRightOf="@+id/tv2"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>

image.png
链条分为水平链条和竖直链条两种,分别用 layout_constraintHorizontal_chainStyle 和 layout_constraintVertical_chainStyle 两个属性来设置
属性值有以下三种:

  • spread
  • spread_inside
  • packed

默认值为 spread
可以通过下图来理解各个参数值的含义
image.png
当参数值为 spread 以及控件宽度非 0 时

1
2
android:layout_width="wrap_content"
app:layout_constraintHorizontal_chainStyle="spread"

image.png

当参数值为 spread 以及控件宽度为 0 时

1
2
android:layout_width="0dp"
app:layout_constraintHorizontal_chainStyle="spread"

image.png

当参数值为 spread_inside 以及控件宽度非0时

1
2
android:layout_width="wrap_content"
app:layout_constraintHorizontal_chainStyle="spread_inside"

image.png
转自:https://www.jianshu.com/p/b884b8c46584