N_Android

布局/组件

约束布局 (ConstraintLayout)

约束布局ConstraintLayout 是一个ViewGroup, 可以在Api9 以上的Android系统使用它, 它的出现主要是为了解决布局嵌套过多的问题, 以灵活的方式定位和调整小部件.从 Android Studio 2.3 起, 官方的模板默认使用 ConstraintLayout.

属性 说明 layout_constraintLeft_toRightOf 放到相对于某个组件右边; 本组件靠左

假设控件B要放在控件A的右侧, 可以使用 layout_constraintLeft_toRightOf 属性.

<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
     app:layout_constraintLeft_toRightOf="@+id/buttonA" />

属性 说明 android:layout_marginStart 设置一个控件相对另一个控件的外边距:

使用 ConstraintLayout 构建自适应界面

线性布局 (Linear Layout)

LinearLayout 又称线性布局, 该布局应该是 Android 视图设计中最经常使用的布局; 该布局可以使放入其中的组件以水平方式或者垂直方式整齐排列, 通过 android:orientation 属性指定具体的排列方式, 通过 weight 属性设置每个组件在布局中所占的比重;

均等分布

如要创建线性布局,让每个子视图使用大小相同的屏幕空间,请将每个视图的 android:layout_height 设置为 “0dp”(针对垂直布局

不等分布

您也可创建线性布局,让子元素使用大小不同的屏幕空间:

如果有三个文本字段,其中两个声明权重为 1,另一个未赋予权重,那么没有权重的第三个文本字段就不会展开,而仅占据其内容所需的区域。另一方面,另外两个文本字段将以同等幅度展开,填充测量三个字段后仍剩余的空间。 如果有三个文本字段,其中两个字段声明权重为 1,而为第三个字段赋予权重 2(而非 0),那么现在相当于声明第三个字段比另外两个字段更为重要,因此,该字段将获得总剩余空间的一半,而其他两个字段均享余下的空间。

android:layout_weight: 指定该子元素在LinearLayout中所占的权重, 值越大权重越低!

developer.android 线性布局

表格布局 (TableLayout)

表格布局继承自LinearLayout, 通过TableRow 设置行,列数由TableRow中的子控件决定, 直接在TableLayout中添加子控件会占据整个一行.

常用属性: 属性 说明 android:shrinkColumns 设置可收缩的列, 内容过多就收缩显示到第二行 android:stretchColumns 设置可伸展的列, 将空白区域填充满整个列 android:collapseColumns 设置要隐藏的列 列的索引从0开始, shrinkColumns和stretchColumns可以同时设置.

子控件常用属性: 属性 说明 android:layout_column 第几列 android:layout_span 占据列数

宫格布局 (GridLayout)

作为android 4.0 后新增的一个布局,与前面介绍过的TableLayout(表格布局)其实有点大同小异; GridLayout的目的是将多个 View / ViewGroup 按照网格的形式排列起来, 所以大多数的属性都是为了规范一个网格的样式;

跟LinearLayout(线性布局)一样,他可以设置容器中组件的对齐方式 容器中的组件可以跨多行也可以跨多列(相比TableLayout直接放组件,占一行相比较)

在Android 5.1(API Level 21)时引入了android:layout_columnWeight和android:layout_rowWeight来解决平分问题

属性

自身属性

属性 说明 android:alignmentMode 说明: 当设置alignMargins, 使视图的外边界之间进行校准.可以取以下值: alignBounds – 对齐子视图边界/alignMargins – 对齐子视距内容 android:columnCount GridLayout的最大列数 android:rowCount GridLayout的最大行数 android:columnOrderPreserved 当设置为true, 使列边界显示的顺序和列索引的顺序相同.默认是true android:orientation GridLayout中子元素的布局方向.有以下取值: horizontal:水平布局/vertical – 竖直布局 android:rowOrderPreserved 当设置为true, 使行边界显示的顺序和行索引的顺序相同.默认是true android:useDefaultMargins 当设置ture, 当没有指定视图的布局参数时, 告诉GridLayout使用默认的边距.默认值是false

子元素属性

属性 说明 android:layout_column 显示该子控件的列, 以0计 android:layout_columnSpan 该控件所占的列数

android:layout_row 显示该子控件的行 以0计 android:layout_rowSpan 该控件所占的行数, 例如android:layout_rowSpan=“2”,表示当前子控件占两行

android:layout_columnWeight 该控件的列权重, 与android:layout_weight类似, 例如有GridLayout上两列, 都设置android:layout_columnWeight = “1”,则两列各占GridLayout宽度的 一半

android:layout_rowWeight 该控件的行权重, 原理同android:layout_columnWeight

当前 View 占据的空间 android:layout_rowSpan: 设置当前 View 占据几行的空间 android:layout_columnSpan: 设置当前 View 占据几列的空间

控件一些属性

layout_gravity: 设置一些对齐方式, 以及填满容器

ScrollView 有时候,我们在写布局的时,在最外层会套一个ScrollView,以防止内容超出屏幕的时候可以滚动。但如果这时候,内容不足以覆盖整个屏幕时,ScrollView 的android:layout_height=“match_parent”属性是无效的,它始终都是wrap_content,这时可以使用android:fillViewport="true" 和 让它生效。

对齐属性

android:alignParentBottom 若是该值为true,则将该控件的底部和父控件的底部对齐 android:layout_alignParentLeft 若是该值为true,则将该控件的左边与父控件的左边对齐 android:layout_alignParentRight 若是该值为true,则将该控件的右边与父控件的右边对齐 android:layout_alignParentTop 若是该值为true,则将空间的顶部与父控件的顶部对齐 // 控件 android:layout_centerHorizontal 若是值为真,该控件将被至于水平方向的中央 android:layout_centerInParent 若是值为真,该控件将被至于父控件水平方向和垂直方向的中央 android:layout_centerVertical 若是值为真,该控件将被至于垂直方向的中央

android:gravity 用于设置View组件的对齐方式 
android:layout_gravity 用于设置Container组件的对齐方式

自定义标题栏

在Android 3.0中除了我们重点讲解的Fragment外, Action Bar也是一个非常重要的交互元素, Action Bar取代了传统的tittle bar和menu, 在程序运行中一直置于顶部

运行时调用hide()方法也可以隐藏ActionBar, 调用show()方法来显示ActionBar().

ActionBar actionBar = getActionBar();
actionBar.hide();

//自定义标题栏
ActionBar actionBar = getSupportActionBar();
actionBar.setCustomView(R.layout.layout_main_title);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayShowTitleEnabled(false);
 
setContentView(R.layout.activity_main);

https://www.cnblogs.com/guanxinjing/p/9708613.html

资源使用

res/values

位于res/valus目录下, 根元素是标记, 在该元素中使用标记定义字符串.其中name属性来指定字符串的名称(name不可大写)

字符串 (String) 资源

<resources>
        <string  name="ss">.....</string>
</resources></span>

使用: a 在MainActivity中使用:gerResources().getString(R.string.name) b 在TextView中使用:

颜色 (color) 资源

<resources>
        <color  name="red">#FF0000</color>
</resources>

使用: a textview.setTextColor(getResources().getColor(R.color.red)); <TextView android:textcolor="@color/red"/>

尺寸 (dimen) 资源

<resources>
        <dimen  name="txt">20dp</dimen>
</resources>

使用: a textview.setTextSize(getResources().getDimension(R.dimen.title)); b

布局 (layout) 资源

布局文件创建完成后, 可以在java代码或者XML中使用

使用:

a: setContentView (R.layout.main) 
b: <include layout="@layout/ly"/>

res/drawable

\drawable-v24 \drawable

在Android开发中我们是用Drawable类来Drawable类型资源的.

Drawable资源一般存储在应用程序目录的\res\drawable目录下,当然依据分辨率的高低可以分别存储不同分辨率的资源到如下几个目录:

\res\drawable-hdpi

\res\drawable-ldpi

\res\drawable-mdpi

\res\drawable-xdpi

使用

在java

getDrawable(R.drawable.filename)
getResources().getDrawable(R.drawable.filename,null)
imageView.getDrawable()
imageView.setImageResource(R.drawable.filename)
imageView.setImageDrawable(drawable)

在xml

@drawable/filename

异步任务/非主线程更新UI

异步任务

 AsyncTask syncTask = new AsyncTask(){
    @Override
    protected Object doInBackground(Object[] objects) {
        //TODO 耗时任务  返回结果
        return null;
    }
    @Override
    protected void onPostExecute(Object result) {
        //完成后接受 结果
    }
};
//开始异步, 可提交参数, 就是上面的 Object[] objects
syncTask.execute(arg1,arg2);

取消

值得注意的是, 调用cancel(true)函数需要注意以下几点:

  1. 当AsyncTask已经完成, 或则以及被取消, 亦或其他原因不能被取消, 调用cancel()会失败;

  2. 如果AsyncTask还没有开始执行, 调用cancel(true)函数后, AsyncTask不会再执行;

  3. 如果AsyncTask已经开始执行, 参数mayInterruptIfRunning决定是否立即stop该Task;

  4. 调用cancel()后, doInBackground()完成后, 不再调用onPostExecute(), 而是执行onCancelled();

非主线程更新UI

使用Handlerpost方法,传入一个Runnable接口, 里面run方法 就可以更新UI了

android.os.Handler handler=new Handler();//在子线程创建 android.os.Handler handler=new Handler(Looper.getMainLooper());
handler.post(
    new Runnable(){
          @Override
        public void run(){
            //更新UI
           iv.setImageBitmap(baseBitmap);  
        }
    }
)

非阻塞弹窗

  • 非阻塞弹窗
AlertDialog.Builder builder  = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("确认" ) ;
builder.setMessage("HTTP结果="+result.toString()+";\r\n"+"本地结果="+MainActivity.this.getHash(MainActivity.this) ) ;
builder.setPositiveButton("是" ,  null );
builder.show();

AlertDialog(简单对话框)

确认对话框

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("确认")
       .setMessage("确定要执行此操作吗?")
       .setPositiveButton("确认", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
               // 用户点击确认时的操作
           }
       })
       .setNegativeButton("取消", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
               // 用户点击取消时的操作
           }
       });
AlertDialog alertDialog = builder.create();
alertDialog.show();
 

列表对话框

显示一个列表供用户选择。

final CharSequence[] items = {"选项1", "选项2", "选项3"};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("选择一个选项")
       .setItems(items, new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int index) {
               // 用户选择列表项时的操作
               // index 是用户选择的索引
           }
       });
AlertDialog alertDialog = builder.create();
alertDialog.show();
 

自定义布局 (xml)

  // 创建弹窗
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MainActivity.this);
LayoutInflater inflater = MainActivity.this.getLayoutInflater();
View dialogView = inflater.inflate(R.layout.dialog_layout, null);
dialogBuilder.setView(dialogView);
 
EditText inputEditText = dialogView.findViewById(R.id.dialog_input);
Button okButton = dialogView.findViewById(R.id.dialog_button);
 
// 设置弹窗按钮点击事件
okButton.setOnClickListener(new View.OnClickListener() {
	@Override
	public void onClick(View v) {
		String userInput = inputEditText.getText().toString();
		// 在这里处理用户输入
		// 例如,可以将输入内容显示在界面上
		// 这里仅仅简单打印用户输入
		System.out.println("User input: " + userInput);
	
		// 关闭弹窗
		alertDialog.dismiss();
	}
});
 
// 创建并显示弹窗
AlertDialog alertDialog = dialogBuilder.create();
alertDialog.show();

自定义布局 (codeing)

val builder = AlertDialog.Builder(this)
 
// LinearLayout 布局容器 - 垂直
val layout = LinearLayout(this)
layout.orientation = LinearLayout.VERTICAL
layout.setPadding(20, 20, 20, 20) // 设置内边距
builder.setTitle("提示")
builder.setView(layout)
val dialog = builder.create()
 
// TextView
val textView = TextView(this)
val lng: String;
val lat: String
if (point == null){
	lng =  "%.6f".format(tapEvent.mapPoint?.x);
	lat =  "%.6f".format(tapEvent.mapPoint?.y);
	textView.text = "位置: ${lng}, ${lat}"
}else{
	lng = point.lng.toString();
	lat = point.lat.toString();
	textView.text = "样点: ${point.name}"
}
textView.setPadding(30, 20, 0, 20) // 文本与下元素之间的间距
textView.textAlignment = View.TEXT_ALIGNMENT_CENTER // 文本居中
layout.addView(textView)
 
// LinearLayout 布局容器 - 水平
val buttonLayout = LinearLayout(this)
buttonLayout.orientation = LinearLayout.HORIZONTAL
buttonLayout.weightSum = 2f // 分配权重,使按钮平均分布
 
// Button
val button1 = Button(this)
button1.text = "导航到此处"
button1.layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) // 设置权重
button1.setOnClickListener {
	toNavi(lat,lng);
}
buttonLayout.addView(button1)
// Button
val button2 = Button(this)
button2.text = "查询图层数据"
button2.layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) // 设置权重
button2.setOnClickListener {
	dialog.dismiss()
	lifecycleScope.launch(Dispatchers.Main) {//协程
		showLayerDetail(tapEvent)
	}
}
buttonLayout.addView(button2)
layout.addView(buttonLayout)
//
dialog.show()

一个 UI loading 动画库

dependencies {
   ...
  compile 'com.wang.avi:library:2.1.3'
  
}

项目地址

layout_sample_view_load.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:background="?android:attr/colorButtonNormal"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center">
        <com.wang.avi.AVLoadingIndicatorView
            android:id="@+id/avi"
            style="@style/AVLoadingIndicatorView.Large"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="vertical">
    </LinearLayout>
 
</LinearLayout>

SampleViewActivity.java

public class SampleViewActivity extends AppCompatActivity implements SampleCallback {
    private static final String TAG =  "SampleViewActivity";
    private AVLoadingIndicatorView avi;
    /**
     * 样品 信息 是否 从本地
     */
    private boolean fromLocal = false;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE); //去除这个Activity的标题栏
        setContentView(R.layout.layout_sample_view_load);
    ...
    // 耗时完成后调用 动态加载最终布局
    public void accept(final SampleBean bean) {
        LayoutInflater layout=this.getLayoutInflater();
        View view=layout.inflate(R.layout.layout_sample_view, null);
        setContentView(view);
        Button button = (Button)view.findViewById(R.id.but_tip);
    }