Android 是一个基于Linux 内核的自由及开发源代码的操作系统
SDK :软件开发工具包,将App源码编译为可执行的App应用
日志级别
XML:布局文件
内置了SQlite 数据库
项目:一个项目下可以有多个模块
模块:编译运行App
gradle :自动化构建工具,依赖、打包、部署、发布、各种渠道的差异管理工作
build.gradle
工程级别
模块级别 build.gradle (Module: Demo1.app)
// 插件的包
plugins {
id 'com.android.application'
}
android {
// 指定SDk版本号 33 就是Android API 33.0编译
compileSdk 33
defaultConfig {
//app包名
applicationId "com.example.demo1"
//app运行的最小SDK版本,28最少要到9.0以上运行
minSdk 28
//目标设备的版本号,最希望app运行在哪个版本上
targetSdk 33
//指定app的应用版本号
versionCode 1
//应用版本名称
versionName "1.0"
//单元测试
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
//app的依赖信息
dependencies {
//appcompat兼容库 版本号
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
//单元测试编译的junit版本号
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
AndroidManifest.xml
清单文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.demo1">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
application>
manifest>
利用 XML (activity_main.xml)标记描绘应用界面,实用Java(MainActivity.class)代码写程序逻辑
创建完整页面:
简单的页面调整
布局文件,两个布局文件 ,从 2 跳到 1
文本内容可以在string.xml 文件中设置,在布局文件中调用链接过来就可以完成
<resources>
<string name="app_name">Demo1string>
<string name="demo02">Android DEVstring>
resources>
创建Java代码,在1 的Java代码中设置跳转代码
Button button = findViewById(R.id.dragPage);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 页面跳转
Intent intent = new Intent();
// 结合上下文
intent.setClass(MainActivity.this,MainActivity2.class);
startActivity(intent);
}
});
注册页面 AndroidManifest.xml
添加一个activity标签
<activity android:name=".MainActivity2" />
XML 布局文件中:android :text 设置文本
<TextView
android:id="@+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello"/>
Java代码中调用文本视图对象的 setText 方法设置文本
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置掌控的页面
setContentView(R.layout.activity_text_view);
TextView view1 = findViewById(R.id.view1);
view1.setText(R.string.hello);
}
在两种方式中都尽量不要直接写文本内容
可以在 string.xml 文件中定义一个文本常量,再进行调用
<resources>
<string name="app_name">SimpleControlstring>
<string name="hello">你好世界未来与你相伴!!!string>
resources>
XML 布局文件中:android :textSize 设置文本大小
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> 垂直布局
<TextView
android:id="@+id/tv_px"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello"
android:textSize="30px"/>
<TextView
android:id="@+id/tv_dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello"
android:textSize="30dp"/>
<TextView
android:id="@+id/tv_sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello"
android:textSize="30sp"/>
LinearLayout>
Java代码中调用文本视图对象的 setTextSize 方法设置文本大小
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_text_size);
TextView s1 = findViewById(R.id.tv_dp);
s1.setTextSize(50);
}
单位
XML 布局文件中:android :textSize 设置文本颜色
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_color1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello"
android:textColor="@color/black"
android:background="@color/purple_500"
android:textSize="32sp" />
<TextView
android:id="@+id/tv_color2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello"
android:textColor="@color/green"
android:background="@color/gray"
android:textSize="30dp" />
LinearLayout>
Java代码中调用文本视图对象的 setTextColor 方法设置文本颜色
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.TextView;
public class TextColorActivity extends AppCompatActivity {
@SuppressLint("ResourceAsColor")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_text_color);
// 从布局文件获取id
TextView viewColor1 = findViewById(R.id.tv_color1);
// 设置系统自带的颜色
viewColor1.setTextColor(Color.RED);
//设置文本背景颜色
viewColor1.setBackgroundColor(R.color.gray);
//设置文本,是系统的颜色
viewColor1.setBackgroundColor(Color.GREEN);
}
}
颜色常量
<resources>
<color name="purple_200">#FFBB86FCcolor>
<color name="purple_500">#FF6200EEcolor>
<color name="purple_700">#FF3700B3color>
<color name="teal_200">#FF03DAC5color>
<color name="teal_700">#FF018786color>
<color name="black">#FF000000color>
<color name="white">#FFFFFFFFcolor>
<color name="green">#058531color>
<color name="gray">#666666color>
resources>
布局文件中设置宽:android:layout_with 高:android:layout_height
取值
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/black"
android:text="@string/hello"
android:textColor="@color/green"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/gray"
android:text="@string/hello"
android:textColor="@color/green"
android:textSize="20sp" />
<TextView
android:layout_width="250dp"
android:layout_height="50dp"
android:background="@color/white"
android:text="@string/hello"
android:textColor="@color/green"
android:textSize="20sp" />
LinearLayout>
效果图:
Java代码设置宽高,确保 xml 布局文件中的 wrap_content
public class ViewBorderActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_border);
TextView tv_wh = findViewById(R.id.wh);
//获取布局参数
ViewGroup.LayoutParams params = tv_wh.getLayoutParams();
//修改布局参数中的宽高,默认dp,需要把dp转换为px数值
params.width = Util.dip2px(this,300);
//设置布局参数
tv_wh.setLayoutParams(params);
}
}
Util.java
类
public class Util {
// 根据手机分辨率 从 dp 转换 为px像素 ,通过上下文
public static int dip2px(Context context, float dpValue) {
// 获取当前手机的像素密度(1dp = ?px)
float scale = context.getResources().getDisplayMetrics().density;
//四舍五入
return (int) (dpValue * scale + 0.5f);
}
}
视图依赖继承关系
间距fang’shi
采用 layout_margin(外间距)属性,它指定了当前视图与周围平级视图之间的距离。
采用 padding(内间距)属性,它指定了当前视图与内部下级视图之间的距离。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#00AAF0"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="30dp"
android:background="@color/yellow"
android:padding="30dp"> 内层的视图与这个视图的间距
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/red">
View>
LinearLayout>
LinearLayout>
对齐方式
两者的取值:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="@color/red"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_gravity="bottom"
android:layout_margin="15dp"
android:layout_weight="1"
android:background="@color/yellow"
android:gravity="left|bottom"
android:padding="10dp">
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@color/teal_200">
View>
LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_gravity="top"
android:layout_margin="15dp"
android:layout_weight="1"
android:background="@color/yellow"
android:gravity="right|top" 在外层设置内层的位置
android:padding="10dp">
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@color/teal_200">
View>
LinearLayout>
LinearLayout>
排列方式
不指定orientation 的属性,默认为水平布局
指的是线性布局的下级视图各自拥有多大比例的宽高
不是在LinearLayout节点设置,而在线性布局的直接下级视图设置,该下级视图占据的宽高比例
layout_width = 0dp :表示水平方向宽高比例
layout_height =0dp:表示垂直方向的宽高比例
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="@color/green">
TextView>
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="@color/green">
TextView>
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="@color/red">
TextView>
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="@color/red">
TextView>
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="@color/yellow">
TextView>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="5"
android:background="@color/yellow">
TextView>
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="200dp"
android:layout_height="0dp"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="@color/red">
TextView>
<TextView
android:layout_width="200dp"
android:layout_height="0dp"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="@color/red">
TextView>
LinearLayout>
LinearLayout>
效果图
相对布局的下级视图位置有其他视图决定,确定下级视图的参照物
默认在 RelativeLayout 内部左上角
位置:
<TextView
android:id="@+id/parent1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/blue"
android:text="上级位置"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/Re_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@color/gray"
android:text="在上级中间"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/parent1_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:background="@color/green"
android:text="我在上级的右边"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/parent_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@color/green"
android:text="在上级下面"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/parent_bottom_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerInParent="true"
android:background="@color/yellow"
android:text="在上级中间下面"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/bottom_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_centerInParent="true"
android:background="@color/green"
android:text="在上级右下角"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/center_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/Re_center"
android:layout_toLeftOf="@id/Re_center"
android:background="@color/red"
android:text="在中间左边"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/center_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/Re_center"
android:layout_toRightOf="@id/Re_center"
android:background="@color/red"
android:text="在中间右边"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/center_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/Re_center"
android:layout_alignLeft="@id/Re_center"
android:background="@color/red"
android:text="在中间上边"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/center_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/Re_center"
android:layout_alignRight="@id/Re_center"
android:background="@color/red"
android:text="在中间下边"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/parent_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:background="@color/yellow"
android:text="上级垂直中间"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/parent_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:background="@color/yellow"
android:text="在上级中间"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/right_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@id/parent1_right"
android:layout_centerVertical="true"
android:background="@color/yellow"
android:text="右边垂直中间"
android:textColor="@color/black"
android:textSize="15sp" />
设置一个两行两列的
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnCount="2"
android:rowCount="2">
<TextView
android:layout_width="0dp" 列的对齐权重
android:layout_height="wrap_content"
android:layout_columnWeight="1" 列的权重
android:layout_margin="2dp"
android:background="@color/red"
android:gravity="center"
android:text="红色"
android:textColor="@color/black" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:layout_margin="2dp"
android:background="@color/green"
android:gravity="center"
android:text="绿色" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:layout_margin="2dp"
android:background="@color/blue"
android:gravity="center"
android:text="蓝色" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:layout_margin="2dp"
android:background="@color/purple_200"
android:gravity="center"
android:text="紫色" />
GridLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="200dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent">
<View
android:layout_width="500dp"
android:layout_height="match_parent"
android:background="@color/green" />
<View
android:layout_width="50dp"
android:layout_height="match_parent"
android:background="@color/yellow"
/>
LinearLayout>
HorizontalScrollView>
<ScrollView
android:layout_width="250dp"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="500dp"
android:background="@color/blue"
/>
<View
android:layout_width="match_parent"
android:layout_height="500dp"
android:background="@color/purple_200"
/>
LinearLayout>
ScrollView>
LinearLayout>
由TextView演变而来
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/blue"
android:gravity="center"
android:text="原始的TextView"
android:textColor="@color/red"
android:textSize="20sp">
TextView>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="doClick"
android:text="dianji"
android:textAllCaps="false"
android:textColor="@color/red"
android:textSize="20sp" />
<TextView
android:id="@+id/view_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/gray"
android:text="点击结果"
android:textColor="@color/red"
android:textSize="20sp">
TextView>
获取当前时间工具类
public class DataUtil {
public static String getNowTime(){
//获取当前的时间
SimpleDateFormat sdf = new SimpleDateFormat("yy/mm/dd HH:mm:ss");
return sdf.format(new Date());
}
}
public class ButtomStyleActivity extends AppCompatActivity {
private TextView time_result;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_buttom_style);
time_result = findViewById(R.id.view_time);
}
/**
* 点击的方法
* @param view
*/
public void doClick(View view){
String str = String.format("%s 点击了 %s", DataUtil.getNowTime(), ((Button) view).getText());
time_result.setText(str);
}
}
时间不是本机时间,二十虚拟手机时间
监听器,意思是专门监听控件的动作行为。只有控件发生了指定的动作,监听器才会触发开关去执行对应的代码逻辑。
按钮控件两种常用的监听器
点击监听器,通过 setOnClickListener 方法设置。按钮被按住少于500毫秒时,会触发点击事件。
public class ButtonClickActivity extends AppCompatActivity implements View.OnClickListener{
private TextView result;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_button_click);
result = findViewById(R.id.tv_result);
Button b1 = findViewById(R.id.single_button);
b1.setOnClickListener(new MyOnClickListener(result));
Button public_button = findViewById(R.id.public_button);
public_button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.public_button){
String str = String.format("%s 点击了 %s", DataUtil.getNowTime(), ((Button) v).getText());
result.setText(str);
}
}
/**
* 实现监听方法 静态内部类:减少内存泄露
*/
static class MyOnClickListener implements View.OnClickListener{
private final TextView result;
public MyOnClickListener(TextView result) {
this.result = result;
}
@Override
public void onClick(View v) {
String str = String.format("%s 点击了 %s", DataUtil.getNowTime(), ((Button) v).getText());
result.setText(str);
}
}
}
<Button
android:id="@+id/single_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="公共事件"
android:background="@color/blue"
android:layout_margin="5dp"
android:textSize="20sp" />
<Button
android:id="@+id/public_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/blue"
android:layout_margin="5dp"
android:text="指定点击监听器"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_result"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="2dp"
android:text="显示内容结果"
android:textColor="@color/black"
android:textSize="20sp" />
长按监听器,通过 setOnLongClickListener 方法设置。按钮被按住超过500毫秒时,会触发长按事件。
public class ButtonLongClickActivity extends AppCompatActivity {
@SuppressLint("WrongViewCast")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_button_long_click);
Button long_click = findViewById(R.id.long_click);
TextView result = findViewById(R.id.tv_result);
//lambda表达式
long_click.setOnLongClickListener(v -> {
String str = String.format("%s 点击了 %s", DataUtil.getNowTime(), ((Button) v).getText());
result.setText(str);
return true;
});
}
}
两种状态
是否允许点击
public class ButtonEanbleActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn_test;
private TextView tv_result1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_button_eanble);
Button btn_enable = findViewById(R.id.btn_enable);
Button btn_disable = findViewById(R.id.btn_disable);
btn_test = findViewById(R.id.btn_test);
tv_result1 = findViewById(R.id.tv_result1);
//监控事件
btn_enable.setOnClickListener(this);
btn_disable.setOnClickListener(this);
btn_test.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
//启动按钮
case R.id.btn_enable:
btn_test.setEnabled(true);
btn_test.setTextColor(Color.BLACK);
break;
//禁用按钮
case R.id.btn_disable:
btn_test.setEnabled(false);
btn_test.setTextColor(Color.GRAY);
break;
//测试按钮
case R.id.btn_test:
String str = String.format("%s 点击了 %s", DataUtil.getNowTime(), ((Button) v).getText());
tv_result1.setText(str);
break;
}
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_enable"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:text="启动测试按钮"
android:textColor="@color/black"
android:textSize="20sp" />
<Button
android:id="@+id/btn_disable"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:text="禁用测试按钮"
android:textColor="@color/black"
android:textSize="20sp" />
LinearLayout>
<Button
android:id="@+id/btn_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:enabled="false"
android:text="测试按钮"
android:textColor="@color/gray"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_result1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:gravity="center"
android:text="查看测试结果"
android:textColor="@color/black"
android:textSize="20sp" />
LinearLayout>
设置方式
scaleType属性
ImageButton与Button区别
imageButton与ImageView区别
取消掉按钮的默认背景颜色
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/white"
android:drawableLeft="@drawable/info"
android:drawablePadding="5dp"
android:text="图标左边" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/white"
android:drawableRight="@drawable/waring"
android:drawablePadding="5dp"
android:text="警告"
android:textColor="@color/red"
android:textSize="20sp" />
计算器的界面分为两大部分,第一部分是上方的计算表达式,既包括用户的按键输入,也包括计算结果数字;第二部分是下方的各个按键,例如:从0到9的数字按钮、加减乘除与等号、正负号按钮、小数点按钮、求倒数按钮、平方按钮、开方按钮以及退格、清空、取消等控制按钮
从当前页面跳转到新页面
startActivity(new Intent(源页面.this,目标页面.class))
从当前页面回到上一个页面,相当于关闭当前页面
finish();结束当前活动页面
public class ActStartActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_act_start);
findViewById(R.id.next_page).setOnClickListener(this);
}
@Override
public void onClick(View v) {
startActivity(new Intent(this, ActFinishActivity.class));
}
}
public class ActFinishActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_act_finish);
findViewById(R.id.back_button).setOnClickListener(this);
findViewById(R.id.back_jiantou).setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.back_button ||v.getId() == R.id.back_jiantou ){
finish();
}
}
}
生命周期图
如果一个Activity已经启动过,并且存在当前应用的Activity任务栈中,启动模式为singleTask,singleInstance或singleTop(此时已在任务栈顶端),那么在此启动或回到这个Activity的时候,不会创建新的实例,也就是不会执行onCreate方法,而是执行
onNewlntent方法。
活动状态变迁
启动模式在清单文件中设置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u0fzjFcH-1665663924472)(AndroidApp.assets/image-20220808105436837.png)]
应用场景
应用场景
Inten组成部分
三种构造方法:
@Override
public void onClick(View v) {
//1.
startActivity(new Intent(this, ActFinishActivity.class));
//2.
Intent intent = new Intent();
intent.setClass(this,ActFinishActivity.class);
//3.
Intent intent = new Intent();
ComponentName componentName = new ComponentName(this, ActFinishActivity.class);
intent.setComponent(componentName);
startActivity(intent);
}
隐式Intent
常用动作
@Override
public void onClick(View v) {
String phoneNo = "11112515";
Intent intent = new Intent();
switch (v.getId()) {
case R.id.jump_call_page:
//设置意图作为动作准备拨号
intent.setAction(Intent.ACTION_DIAL);
//声明一个拨号的Uri
Uri uri = Uri.parse("tel" + phoneNo);
intent.setData(uri);
startActivity(intent);
break;
case R.id.jump_info_page:
//设置意图作为动作准备拨号
intent.setAction(Intent.ACTION_SENDTO);
//声明一个拨号的Uri
Uri uri_sen_info = Uri.parse("info" + phoneNo);
intent.setData(uri_sen_info);
startActivity(intent);
break;
case R.id.jump_accpect_info_page:
//设置意图作为动作准备拨号
intent.setAction(Intent.ACTION_ANSWER);
//声明一个拨号的Uri
Uri uri_answer = Uri.parse("info" + phoneNo);
intent.setData(uri_answer);
startActivity(intent);
break;
}
}
Intent使用Bundle对象存放待传递的数据信息
Bundle 对象的数据类型
在代码中发送消息包裹,调用意图对象的putExtras方法,即可存入消息包裹。
在代码中接收消息包裹,调用意图对象的getExtras方法,即可取出消息包裹。
@Override
public void onClick(View v) {
Intent intent = new Intent(this, ActReceiveActivity.class);
Bundle bundle = new Bundle();
//发送的数据
bundle.putString("request_time", DataUtil.getNowTime());
bundle.putString("request_content",sent_text.getText().toString());
//存入包裹
intent.putExtras(bundle);
startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_act_receive);
TextView receive_text = findViewById(R.id.receive_text);
//从上一个页面中传来的意图中获取包裹
Bundle bundle = getIntent().getExtras();
String request_time = bundle.getString("request_time");
String request_content = bundle.getString("request_content");
String format = String.format("收到消息:\n请求时间:%s\n请求内容:%s", request_time, request_content);
receive_text.setText(format);
}
请求信息
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.example.activity.utils.DataUtil;
public class ActRequestActivity extends AppCompatActivity implements View.OnClickListener {
private String Request = " 可以去你家睡吗?";
private ActivityResultLauncher<Intent> register;
private TextView response_show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_act_request);
TextView request_text = findViewById(R.id.request_text);
request_text.setText("待发送的消息:" + Request);
response_show = findViewById(R.id.response_show);
//按钮监听
findViewById(R.id.request_but).setOnClickListener(this);
//response回调方法
register = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result != null) {
Intent intent = result.getData();
if (intent != null && result.getResultCode() == Activity.RESULT_OK) {
Bundle bundle = intent.getExtras();
String request_time = bundle.getString("request_time", DataUtil.getNowTime());
String request_content = bundle.getString("request_content");
String desc = String.format("request收到的消息:\n请求时间:%s\n请求内容:%s", request_time, request_content);
//把返回消息的详细信息显示
response_show.setText(desc);
}
}
});
}
@Override
public void onClick(View v) {
//构建显示意图
Intent intent = new Intent(this, ActResponseActivity.class);
Bundle bundle = new Bundle();
//发送的数据
bundle.putString("request_time", DataUtil.getNowTime());
bundle.putString("request_content", Request);
intent.putExtras(bundle);
//存入包裹
intent.putExtras(bundle);
register.launch(intent);
}
}
响应信息
public class ActResponseActivity extends AppCompatActivity implements View.OnClickListener {
private static final String Response_back = "不可以!!";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_act_response);
TextView request_text = findViewById(R.id.request_text);
//从上一个页面中传来的意图中获取包裹
Bundle bundle = getIntent().getExtras();
String request_time = bundle.getString("request_time");
String request_content = bundle.getString("request_content");
String format = String.format("response收到的消息:\n请求时间:%s\n请求内容:%s", request_time, request_content);
//请求的信息显示在指定的文本上
request_text.setText(format);
findViewById(R.id.response_but).setOnClickListener(this);
TextView back_text = findViewById(R.id.response_back);
back_text.setText("要返回给request的消息:" + Response_back);
}
@Override
public void onClick(View v) {
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("request_time", DataUtil.getNowTime());
bundle.putString("request_content", Response_back);
intent.putExtras(bundle);
setResult(Activity.RESULT_OK, intent);
//结束
finish();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_read_sourse_striing);
TextView resource_string = findViewById(R.id.string_source);
//从string.xml文件中获取字符串值
String go_home = getString(R.string.go_home);
resource_string.setText(go_home);
}
<resources>
<string name="app_name">Activitystring>
<string name="go_home">我想和你回家!!!string>
resources>
<meta-data android:name="go" android:value="和你回家"/>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_meta_data);
TextView meta_source_value = findViewById(R.id.meta_source_value);
//获取包管理器
PackageManager packageManager = getPackageManager();
//
try {
ActivityInfo packageInfo = packageManager.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
Bundle bundle = packageInfo.metaData;
String go = bundle.getString("go");
meta_source_value.setText(go);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
新建 xml文件夹
shortcut.xml
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:shortcutId="first"
android:shortcutLongLabel="@string/first_long"
android:shortcutShortLabel="@string/first_short">
<intent
android:action="android.intent.action.VIEW"
android:targetClass="com.example.activity.ActStartActivity"
android:targetPackage="com.example.activity" />
<categories android:name="android:shortcuts.conversation" />
shortcut>
<shortcut
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:shortcutId="second"
android:shortcutLongLabel="@string/second_long"
android:shortcutShortLabel="@string/second_short">
<intent
android:action="android.intent.action.VIEW"
android:targetClass="com.example.activity.ActSentActivity"
android:targetPackage="com.example.activity" />
<categories android:name="android:shortcuts.conversation" />
shortcut>
<shortcut
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:shortcutId="third"
android:shortcutLongLabel="@string/third_long"
android:shortcutShortLabel="@string/third_short">
<intent
android:action="android.intent.action.VIEW"
android:targetClass="com.example.activity.JumpFirstActivity"
android:targetPackage="com.example.activity" />
<categories android:name="android:shortcuts.conversation" />
shortcut>
shortcuts>
strings.xml
<string name="first_short">firststring>
<string name="first_long">启停活动string>
<string name="second_short">secondstring>
<string name="second_long">发送数据string>
<string name="third_short">thirdstring>
<string name="third_long">跳转页面string>
需要的页面必须存在(配置文件中,列表清单文件 AndroidManifest.xml
),才可以进行操作
系统框架
几何图形
Shape的主要包括四种图形
图形的规格定义
size(尺寸),它描述了形状图形的宽高尺寸。
stroke(边框),它描述了形状图形的描边规格
corners(圆角),它描述了形状图形的圆角大小。
solid(填充),它描述了形状图形的填充色彩。
padding(间隔),它描述了形状图形与周围边界的间隔
gradient(渐变),它描述了形状图形的颜色渐变。
public class DrawableShapeActivity extends AppCompatActivity implements View.OnClickListener {
private View view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drawable_shape);
view = findViewById(R.id.v_content);
findViewById(R.id.circular).setOnClickListener(this);
findViewById(R.id.ellipse).setOnClickListener(this);
//默认情况下,为圆角矩形
view.setBackgroundResource(R.drawable.shape_circular);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.circular:
view.setBackgroundResource(R.drawable.shape_circular);
break;
case R.id.ellipse:
view.setBackgroundResource(R.drawable.shape_ellipse);
break;
}
}
}
shape_circular.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/gold"/>
<stroke
android:width="2dp"
android:color="@color/red"
/>
<corners android:radius="50dp"/>
shape>
shape_ellispce.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/purple_500" />
<stroke
android:width="2dp"
android:color="@color/red" />
shape>
必须的 .png 格式的图片
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/view1" android:state_pressed="true" />
<item android:drawable="@drawable/btn1" />
selector>
继承关系
CompoundButton 在 XML 文件中主要使用下面两个属性。
CompoundButton 在 Java 代码中主要使用下列4种方法。
设置股癣状态的监听器,并且设置内容显示
public class CheckBoxActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_check_box);
CheckBox system_checkbox = findViewById(R.id.system_checkbox);
CheckBox custom_checkbox = findViewById(R.id.custom_checkbox);
system_checkbox.setOnCheckedChangeListener(this);
custom_checkbox.setOnCheckedChangeListener(this);
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
String desc = String.format("你%s了这个CheckBox", isChecked ? "勾选" : "取消勾选");
buttonView.setText(desc);
}
}
Switch 控件属性
RadioGroup 实质上是个布局,同一组RadioButton都要放在同一个 RadioGroup 节点下
RadioGroup的常用方法
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请选择你的性别" />
<RadioGroup
android:id="@+id/select_gender"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<RadioButton
android:id="@+id/sex_male"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="男" />
<RadioButton
android:id="@+id/sex_female"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="女" />
RadioGroup>
<TextView
android:id="@+id/show_sex"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp" />
LinearLayout>
public class RadioHorizaontalActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {
private TextView show_sex;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_radio_horizaontal);
RadioGroup select_gender = findViewById(R.id.select_gender);
show_sex = findViewById(R.id.show_sex);
select_gender.setOnCheckedChangeListener(this);
}
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId){
case R.id.sex_male:
show_sex.setText("你是一个man");
break;
case R.id.sex_female:
show_sex.setText("你是一个women");
break;
}
}
}
文本编辑框
EditText 一些方法
监听输入手机号码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">
<EditText
android:id="@+id/phone_num"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="@drawable/edittext_slelect"
android:hint="请输入手机号码"
android:inputType="number"
android:maxLength="11" />
<EditText
android:id="@+id/input_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="@drawable/edittext_slelect"
android:hint="请输入6位密码"
android:inputType="numberPassword"
android:textColor="@color/green"
android:maxLength="6"/>
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登录" />
LinearLayout>
public class EditFocusActivity extends AppCompatActivity implements View.OnFocusChangeListener {
private TextView input_phone;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit_focus);
input_phone = findViewById(R.id.phone_num);
EditText password = findViewById(R.id.input_password);
password.setOnFocusChangeListener(this);
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
String phone = input_phone.getText().toString();
//提示手机号码不足11位
if (TextUtils.isEmpty(phone) || phone.length() < 11){
input_phone.requestFocus();
Toast.makeText(this, "请输入11位手机号码", Toast.LENGTH_SHORT).show();
}
}
}
}
文本位数自动关闭键盘
调用编辑框对象的 addTextChangedListener 方法即可注册文本监听器。
文本监听器的接口名称为 TextWatcher,该接口提供了3个监控方法,如下。
public class EditHideActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit_hind);
EditText input_phone1 = findViewById(R.id.phone_num1);
EditText password1 = findViewById(R.id.input_password1);
input_phone1.addTextChangedListener(new HideTextWather(input_phone1, 11));
password1.addTextChangedListener(new HideTextWather(password1, 6));
}
//编辑框监听器,输入文本达到要求自动隐藏输入法
private class HideTextWather implements TextWatcher {
//编辑框对象
private EditText mView;
//最大长度变量
private int mMaxLenght;
public HideTextWather(EditText mView, int mMaxLenght) {
this.mView = mView;
this.mMaxLenght = mMaxLenght;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
//编辑框输入文本变化之后触发
@Override
public void afterTextChanged(Editable s) {
//获得输入的文本字符串
String str =s.toString();
//输入文本达到11 位或者 6 位时关闭输入法
if (str.length() == mMaxLenght){
//就隐藏输入法键盘
ViewUtil.hideOneInputMethod(EditHideActivity.this,mView);
}
}
}
}
工具类:
public class ViewUtil {
public static void hideOneInputMethod(Activity act, View v) {
// 从系统服务中获取输入法管理器
InputMethodManager imm = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE);
// 关闭屏幕上的输入法软键盘
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
AlertDialog 可以完成常见的交互操作:提示、确定等。
public class AlertDialogActivity extends AppCompatActivity implements View.OnClickListener {
private TextView tv_alert;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alert_dialog);
findViewById(R.id.btn_alert).setOnClickListener(this);
tv_alert = findViewById(R.id.tv_alert);
}
@Override
public void onClick(View v) {
//创建提醒对话框的建造器
AlertDialog.Builder builder = new AlertDialog.Builder(this);
//设置对话框标题
builder.setTitle("尊敬的用户");
//设置对话框文本
builder.setMessage("你不要卸载我啊");
//设置对话框肯定按钮文本及其监听器
builder.setNegativeButton("拜拜,卸载你", ((dialog, which) -> {
tv_alert.setText("我走了,呜呜呜~~~");
}));
//设置对话框否定按钮文本及其监听器
builder.setPositiveButton("再给你一次机会", ((dialog, which) -> {
tv_alert.setText("我会变好的~~~");
}));
// 根据构建起对话框提醒对话框对象
AlertDialog alertDialog = builder.create();
//显示提醒对话框
alertDialog.show();
}
}
public class DatePickerActivity extends AppCompatActivity implements View.OnClickListener, DatePickerDialog.OnDateSetListener {
private DatePicker date;
private TextView tv_date_text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_date_picker);
findViewById(R.id.tv_btn_date).setOnClickListener(this);
findViewById(R.id.tv_btn_date_alert).setOnClickListener(this);
tv_date_text = findViewById(R.id.tv_date_text);
date = findViewById(R.id.date_picker1);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_btn_date:
String desc = String.format("您选择的日期是 %d 年 %d 月 %d 日", date.getYear(), date.getMonth() + 1, date.getDayOfMonth());
tv_date_text.setText(desc);
break;
case R.id.tv_btn_date_alert:
//获取日历的当前时间
Calendar ca = Calendar.getInstance();
DatePickerDialog dialog = new DatePickerDialog(this, this,
ca.get(Calendar.YEAR), ca.get(Calendar.MONTH), ca.get(Calendar.DAY_OF_MONTH));
//显示日期对话框
dialog.show();
break;
}
}
@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
String desc = String.format("您选择的日期是 %d 年 %d 月 %d 日", year, month + 1, dayOfMonth);
tv_date_text.setText(desc);
}
}
public class TimePickerActivity extends AppCompatActivity implements View.OnClickListener, DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener {
private TimePicker time;
private TextView tv_time_text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_time_picker);
findViewById(R.id.tv_btn_time).setOnClickListener(this);
findViewById(R.id.tv_btn_time_alert).setOnClickListener(this);
tv_time_text = findViewById(R.id.tv_time_text);
time = findViewById(R.id.time_picker1);
//设置24 小时
time.setIs24HourView(true);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_btn_time:
String desc = String.format("您选择的时间是 %d 时 %d 分 ", time.getHour(), time.getMinute());
tv_time_text.setText(desc);
break;
case R.id.tv_btn_time_alert:
//获取日历的当前时间
Calendar ca = Calendar.getInstance();
TimePickerDialog dialog = new TimePickerDialog(this, android.R.style.Theme_Holo_Light_DarkActionBar,this,
ca.get(Calendar.HOUR_OF_DAY), ca.get(Calendar.MINUTE), true);
//显示日期对话框
dialog.show();
break;
}
}
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
String desc = String.format("您选择的时间是 %d 时 %d 分 ", hourOfDay, minute);
tv_time_text.setText(desc);
}
@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
}
}
登录方式
区别
密码输入框和验证码输入框的左侧标题以及输入框内部的提示语各不相同
如果是密码登录,则需要支持找回密码;如果是验证码登录,则需要支持向用户手机发送 验证码;
密码登录可以提供记住密码功能,而验证码的数值每次都不一样,无需也没法记住验证码
找回密码
整个登录模块由登录页面和找回密码页面组成,因此这两个页面之间需要进行数据交互。
实际开发中,共享参数经常存储的数据有App的个性化配置信息、用户使用App的行为信息、临时需要保存的片段信息等
package com.example.datastorage;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.view.contentcapture.ContentCaptureSession;
import android.widget.CheckBox;
import android.widget.EditText;
public class ShareWriteActivity extends AppCompatActivity implements View.OnClickListener {
private EditText et_name;
private EditText et_age;
private EditText et_height;
private EditText et_weight;
private CheckBox ck_married;
private SharedPreferences preferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_share_write);
et_name = findViewById(R.id.et_name);
et_age = findViewById(R.id.et_age);
et_height = findViewById(R.id.et_height);
et_weight = findViewById(R.id.et_weight);
ck_married = findViewById(R.id.ck_married);
findViewById(R.id.btn_save).setOnClickListener(this);
preferences = getSharedPreferences("config", Context.MODE_PRIVATE);
reload();
}
private void reload() {
String name = preferences.getString("name", null);
if (name != null) {
et_name.setText(name);
}
int age = preferences.getInt("age", 0);
if (age != 0) {
et_age.setText(String.valueOf(age));
}
float height = preferences.getFloat("height", 0f);
if (height != 0f) {
et_height.setText(String.valueOf(height));
}
float weight = preferences.getFloat("weight", 0f);
if (weight != 0f) {
et_weight.setText(String.valueOf(weight));
}
boolean married = preferences.getBoolean("married", false);
ck_married.setChecked(married);
}
@Override
public void onClick(View v) {
String name = et_name.getText().toString();
String age = et_age.getText().toString();
String height = et_height.getText().toString();
String weight = et_weight.getText().toString();
//编辑器
SharedPreferences.Editor editor = preferences.edit();
editor.putString("name", name);
editor.putInt("age", Integer.parseInt(age));
editor.putFloat("height", Float.parseFloat(height));
editor.putFloat("weight", Float.parseFloat(weight));
editor.putBoolean("married", ck_married.isChecked());
editor.commit();
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="姓名:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_name"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginTop="3dp"
android:layout_marginBottom="3dp"
android:layout_weight="1"
android:background="@drawable/edittext_select"
android:hint="请输入姓名"
android:inputType="text"
android:maxLength="12"
android:textColor="@color/black"
android:textSize="17sp" />
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="年龄:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_age"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginTop="3dp"
android:layout_marginBottom="3dp"
android:layout_weight="1"
android:background="@drawable/edittext_select"
android:hint="请输入年龄"
android:inputType="number"
android:maxLength="2"
android:textColor="@color/black"
android:textSize="17sp" />
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_height"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="身高:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_height"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginTop="3dp"
android:layout_marginBottom="3dp"
android:layout_weight="1"
android:background="@drawable/edittext_select"
android:hint="请输入身高"
android:inputType="number"
android:maxLength="3"
android:textColor="@color/black"
android:textSize="17sp" />
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_weight"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="体重:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_weight"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginTop="3dp"
android:layout_marginBottom="3dp"
android:layout_weight="1"
android:background="@drawable/edittext_select"
android:hint="请输入体重"
android:inputType="numberDecimal"
android:maxLength="5"
android:textColor="@color/black"
android:textSize="17sp" />
LinearLayout>
<CheckBox
android:id="@+id/ck_married"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:gravity="center"
android:text="已婚"
android:textColor="@color/black"
android:textSize="17sp" />
<Button
android:id="@+id/btn_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存到共享参数"
android:textColor="@color/black"
android:textSize="17sp" />
LinearLayout>
edittext_select
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@drawable/shape_edit_focus"/>
<item android:state_focused="false" android:drawable="@drawable/shape_edit_nofocus"/>
selector>
与MySQL一样的操作
管理类,用于数据库层面的操作。
事务类,用于事务层面的操作。
数据处理类,用于数据表层面的操作。
新建一个继承自 SQLiteOpenHelper 的数据库操作类,提示重写 onCreate 和 onUpgrade 两个方 法。
调用SQLiteDatabase的query和rawQuery方法时,返回的都是Cursor对象,因此获取 查询结果要根据游标的指示一条一条遍历结果集合
游标控制类方法,用于指定游标的状态。
游标移动类方法,把游标移动到指定位置。
获取记录类方法,可获取记录的数量、类型以及取值。
SQLite记住密码:
外部存储空间:
Android 的位图工具是Bitmap,App读写Bitmap可以使用性能更好的BufferedOutputS tream和BufferedInputStream
Android还提供了BitmapFactory工具用于读取各种来源的图片,相关方法如下:
decodeResource:该方法可从资源文件中读取图片信息。
decodeFile:该方法可将指定路径的图片读取到Bitmap对象。
decodeStream:该方法从输入流中读取位图数据
适合在Application中保存的全局变量主要有下面3类数据:
用自定义Application的静态成员变量实现全局变量的功能
使用数据库帮助器编码的时候,开发者每次都得手工实现以下代码逻辑:
在使用Room之前,要先修改模块的 build.gradle 文件,往 dependencies 节点添加下面两行配置,表示导入指定版本的Room库:
以录入书籍信息为例,使用Room框架的编码过程分为下列五步:
ContentProvider 为App内部数据提供统一的外部接口,让不同的应用之间得以共享数据
Client App 讲用户输入内容,通过ContenetProvider跨进程通信传递给Server App
ContentProvider只是服务端App存取数据的抽象类,需要在其基础上实现一个完整的内容提供器,并重写下列方法。
Uri(通用资源标识符 Universal Resource Identifer),代表数据操作的地址,每一个Cont entProvider 都会有唯一的地址。ContentProvider 使用的Uri 语法结构如下:
content://authority/data_path/id
ContentProvider只实现服务端App的数据封装,如果客户端App想访问对方的内部 数据,就要通过内容解析器ContentResolver访问
防止某些App滥用权限,允许App在运行时动态检查是否拥有某些权限,没有则会弹出权限访问窗口获取权限
步骤
内容观察器ContentObserver给目标内容注册一个观察器,目标内容的数据一旦发生变 化,观察器规定好的动作马上触发,从而执行开发者预先定义的代码
继承与ContentProvider,对第三方应用暴露文件,授予文件读写权限
FileProvider的配置步骤
继承结构
最简单的适配器,只展示一行文字
数组适配器分成下列步骤
集合当中的数据与条目布局的对应关系
条目与数据集合对应关系
当列表的Item从上方滚出屏幕视角之外时
ListView 新增属性
新增属性方法
GridView拉伸效果
GridView 拉伸模式取值
翻页视图3个常用方法
setAdapter:设置页面项的适配器。适配器用的是PagerAdapter及其子类。
setCurrentItem:设置当前页码,也就是要显示哪个页面。
addOnPageChangeListener:添加翻页视图的页面变更监听器。该监听器需实现接
OnPageChangeListener下的3个方法,
onPageScrollStateChanged:在页面滑动状态变化时触发。
onPageScrolled:在页面滑动过程中触发。
onPageSelected:在选中页面时,即滑动结束后触发。
启动引导页的适配器
好用的记账本必须具备两项基本功能,一项是记录新账单,另一项是查看账单列表。其中账单的记录操 作要求用户输入账单的明细要素,包括账单的发生时间、账单的收支类型(收入还是支出)、账单的交 易金额、账单的事由描述等,据此勾勒简易的账单添加界面如图8-30所示。账单列表页通常分月展示, 每页显示单个月份的账单数据,还要支持在不同月份之间切换。每月的账单数据按照时间从上往下排 列,每行的账单明细则需依次展示账单日期、事由描述、交易金额等信息,然后列表末尾展示当月的账 单合计情况(总共收入多少、总共支出多少)。根据这些要求描绘的账单列表界面原型如图8-31所示。 账单的填写功能对应数据库记录的添加操作,账单的展示功能对应数据库记录的查询操作,数据库记录 还有修改和删除操作,分别对应账单的编辑功能和删除功能。账单的编辑页面原型如图8-32所示,至于 删除操作则由如图8-33所示的提示窗控制,点击“是”按钮表示确定删除,点击“否”按钮表示取消删除。
账单填写页面、账单列表页面
账单编辑页面
伤处账单提示窗
实现日期下拉框
填写账单时间的时候,输入界面默认展示当天日期,用户若想修改账单时间,就要点击日期文本,此时 界面弹出日期选择对话框,待用户选完具体日期,再回到主界面展示选定日期的文本。这种实现方式类 似于下拉框控件Spinner,可是点击Spinner会弹出文本列表对话框,而非日期选择对话框。尽管 Android未提供现成的日期下拉框,但是结合文本视图与日期选择对话框,也能实现类似Spinner的日期 下拉框效果
编辑与删除账单项
需求描述提到既要支持账单的编辑功能,又要支持账单的删除功能,因为账单明细位于列表视图当中, 且列表视图允许同时设置列表项的点击监听器和长按监听器,所以可考虑将列表项的点击监听器映射到 账单的编辑功能,将列表项的长按监听器映射到账单的删除功能,也就是点击账单项时跳到账单的编辑 页面,长按账单项时弹出删除账单的提醒对话框。
合并账单的添加与编辑功能
账单编辑页面仍然跳到了BillAddActivity,然而该页面原本用作账单填写,若想让它同时 支持账单编辑功能,则需从意图包裹取出名为xuhao的字段,得到上个页面传来的序号数值,通过判断 该字段是否为-1,再分别对应处理
Android 中的广播: 发送一条广播,可以被不同的广播接收者所接收,广播接收者收到广播之后,再进行逻辑 处理
(1)活动只能一对一通信;而广播可以一对多,一人发送广播,多人接收处理。
(2)对于发送方来说,广播不需要考虑接收方有没有在工作,接收方在工作就接收广播,不在工作就丢 弃广播。
(3)对于接收方来说,因为可能会收到各式各样的广播,所以接收方要自行过滤符合条件的广播,之后 再解包处理。
先创建意图对象,再调用sendBroadcast方法发送广播
广播发出来之后,还得有设备去接收广播,也就是需要广播接收器。接收器主要规定两个事情,一个是 接收什么样的广播,另一个是收到广播以后要做什么。由于接收器的处理逻辑大同小异,因此Android 提供了抽象之后的接收器基类BroadcastReceiver,开发者自定义的接收器都从BroadcastReceiver派生 而来。新定义的接收器需要重写onReceive方法,方法内部先判断当前广播是否符合待接收的广播名 称,校验通过再开展后续的业务逻辑
通过意图过滤器挑选动作名称一致的广播
(1)一个广播存在多个接收器,这些接收器需要排队收听广播,这意味着该广播是条有序广播。
(2)先收到广播的接收器A,既可以让其他接收器继续收听广播,也可以中断广播不让其他接收器收听。
Intent.ACTION_TIME_TICK
定义一个分钟广播的接收器,并重写接收器的onReceive方法,补充收到广播之后的处理逻 辑。
重写活动页面的onStart方法,添加广播接收器的注册代码,注意要让接收器过滤分钟到达广播 Intent.ACTION_TIME_TICK
重写活动页面的onStop方法,添加广播接收器的注销代码
步骤一,定义一个网络广播的接收器,并重写接收器的onReceive方法,补充收到广播之后的处理逻 辑。
步骤二,重写活动页面的onStart方法,添加广播接收器的注册代码,注意要让接收器过滤网络变更广播 android.net.conn.CONNECTIVITY_CHANGE。
步骤三,重写活动页面的onStop方法,添加广播接收器的注销代码
getType:获取网络类型
getTypeName:获取网络类型的名称。
getSubtype:获取网络子类型。当网络类型为数据连接时,子类型为2G/3G/4G的细分类型,如 CDMA、EVDO、HSDPA、LTE等
getSubtypeName:获取网络子类型的名称。
getState:获取网络状态
系统闹钟定时发送广播,常见的方法:
定时管理器使用了PendingIntent,它与Intent之间的差异主要有下列三点:
手机有竖屏与横屏两种模式,两种屏幕方向不但造成App界面的展示差异,而且竖屏和横 屏切换之际,甚至会打乱App的生命周期
为了避免横竖屏切换时重新加载界面的情况,Android设计了一种配置变更机制,在指定 的环境配置发生变更之时,无需重启活动页面,只需执行特定的变更行为。该机制的实现 过程分为两步:
自定义View的实现方式有以下几种
类型 | 定义 |
---|---|
自定义组合控件 | 多个控件组合成为一个新的控件,方便多处复用 |
继承系统View控件 | 继承自TextView等系统控件,在系统控件的基础功能上进行扩展 |
继承View | 不复用系统控件逻辑,继承View进行功能定义 |
继承系统ViewGroup | 继承自LinearLayout等系统控件,在系统控件的基础功能上进行扩展 |
继承ViewViewGroup | 不复用系统控件逻辑,继承ViewGroup进行功能定义 |
Android中的任何一个布局、任何一个控件其实都是直接或间接继承自View的,如TextView、Button、ImageView、ListView等
每一个视图的绘制过程都必须经历三个最主要的阶段,即初始化、onMeasure()、onLayout()和onDraw()
View坐标
由上图可算出View的高度:
View的源码当中提供了getWidth()和getHeight()方法用来获取View的宽度和高度,其内部方法和上文所示是相同的,我们可以直接调用来获取View得宽高。
View自身的坐标
通过如下方法可以获取View到其父控件的距离。
getTop();获取View到其父布局顶边的距离。
getLeft();获取View到其父布局左边的距离。
getBottom();获取View到其父布局顶边的距离。
getRight();获取View到其父布局左边的距离
无论是我们继承系统View还是直接继承View,都需要对构造函数进行重写,构造函数有多个,至少要重写其中一个才行。
package com.kcs.customview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
public class CustomView extends View {
/**
* 构造函数会在new的时候调用
* @param context
*/
public CustomView(Context context) {
super(context);
}
/**
* 在xml布局文件中使用时自动调用
* @param context
* @param attrs
*/
public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
/**
* 不会自动调用,如果有默认style时,在第二个构造函数中调用
* @param context
* @param attrs
* @param defStyleAttr
*/
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 只有在API版本>21时才会用到
* 不会自动调用,如果有默认style时,在第二个构造函数中调用
* @param context
* @param attrs
* @param defStyleAttr
* @param defStyleRes
*/
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
onMeasure()方法用于测量视图的大小
一个界面的展示可能会涉及到很多次的measure,因为一个布局中一般都会包含多个子视图,每个视图都需要经历一次measure过程。ViewGroup中定义了一个measureChildren()方法来去测量子视图的大小
MeasureSpec
是View的内部类,它封装了一个View的尺寸,在onMeasure()
当中会根据这个MeasureSpec
的值来确定View的宽高。
MeasureSpec
的值保存在一个int值当中。一个int值有32位,前两位表示模式mode
后30位表示大小size
。即MeasureSpec
= mode
+ size
。
在MeasureSpec
当中一共存在三种mode
:UNSPECIFIED
、EXACTLY
和
AT_MOST
。
对于View来说,MeasureSpec
的mode和Size有如下意义
模式 | 意义 | 对应 |
---|---|---|
EXACTLY | 精准模式,View需要一个精确值,这个值即为MeasureSpec当中的Size | match_parent |
AT_MOST | 最大模式,View的尺寸有一个最大值,View不可以超过MeasureSpec当中的Size值 | wrap_content |
UNSPECIFIED | 无限制,View对尺寸没有任何限制,View设置为多大就应当为多大 | 一般系统内部使用 |
// 获取测量模式(Mode)
int specMode = MeasureSpec.getMode(measureSpec)
// 获取测量大小(Size)
int specSize = MeasureSpec.getSize(measureSpec)
// 通过Mode 和 Size 生成新的SpecMode
int measureSpec=MeasureSpec.makeMeasureSpec(size, mode);
View中的onLayout()方法就是一个空方法,因为onLayout()过程是为了确定视图在布局中所在的位置,而这个操作应该是由布局来完成的,即父视图决定子视图的显示位置
ViewGroup中的onLayout()方法竟然是一个抽象方法,这就意味着所有ViewGroup的子类都必须重写这个方法。像LinearLayout、RelativeLayout等布局,都是重写了这个方法,然后在内部按照各自的规则对子视图进行布局的。
/**
* 这里的四个参数l、t、r、b分别代表View的左、上、右、下四个边界相对于其父View的距离。
*
*/
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
//这里通过setFrame或setOpticalFrame方法确定View在父容器当中的位置。
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
//调用onLayout方法。onLayout方法是一个空实现,不同的布局会有不同的实现。
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
}
}
layout()
方法中已经通过setOpticalFrame(l, t, r, b)
或 setFrame(l, t, r, b)
方法对View自身的位置进行了设置,所以onLayout(changed, l, t, r, b)
方法主要是ViewGroup
对子View的位置进行计算。
public void draw(Canvas canvas) {
int saveCount;
// 1. 如果需要,绘制背景
if (!dirtyOpaque) {
drawBackground(canvas);
}
// 2. 有过有必要,保存当前canvas。
final int viewFlags = mViewFlags;
if (!verticalEdges && !horizontalEdges) {
// 3. 绘制View的内容。
if (!dirtyOpaque) onDraw(canvas);
// 4. 绘制子View。
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// 6. 绘制装饰,如滚动条等等。
onDrawForeground(canvas);
// we're done...
return;
}
}
/**
* 1.绘制View背景
*/
private void drawBackground(Canvas canvas) {
//获取背景
final Drawable background = mBackground;
if (background == null) {
return;
}
setBackgroundBounds();
//获取便宜值scrollX和scrollY,如果scrollX和scrollY都不等于0,则会在平移后的canvas上面绘制背景。
final int scrollX = mScrollX;
final int scrollY = mScrollY;
if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
/**
* 3.绘制View的内容,该方法是一个空的实现,在各个业务当中自行处理。
*/
protected void onDraw(Canvas canvas) {
}
/**
* 4. 绘制子View。该方法在View当中是一个空的实现,在各个业务当中自行处理。
* 在ViewGroup当中对dispatchDraw方法做了实现,主要是遍历子View,并调用子类的draw方法,一般我们不需要自己重写该方法。
*/
protected void dispatchDraw(Canvas canvas) {
}
自定义月份选择器
给翻页标签蓝添加新标签
不滚动的列表视图
Handller 的延迟机制
重新绘制视图界面
自定义饼图动画
界面设计
实现代码
通知推送 Notification
通知渠道 NotificationChannel
桌面应用添加消息图标
服务的启动和停止
服务的绑定与 解绑
推送服务到前台
分线程通过Handler 操作界面
异步任务 AsyncTask
异步服务 IntentService
BottomNavigationView 实现底部标签栏
自定义标签栏
结合RadioGroup和ViewPager自定义底部标签栏
工具栏 Toolbar
溢出菜单OverflowMenu
标签布局TabLayout
循环视图RecycleView
布局管理器LayoutManager
动态更新循环视图
下拉刷新布局SwipeRefreshLayout
第二代翻页视图ViewPager2
给ViewPager2集成标签布局
实战项目:电商首页
需求
界面
代码
使用相机拍摄照片
从相册中选取图片
对图片进行简单加工
图像解码器ImageDecoder
使用录音机录制音频
利用MediaPlayer播放音频
利用MediaRecorder录制音频
使用摄像机录制视频
从视频库中选取视频
利用视频视图(VideoView)播放视频
需求
界面设计
实现代码
HTTP接口访问
GET方式调用HTTP接口
POST方式调用HTTP接口
通知栏显示下载进度
主动轮询当前的下载进度
利用POST方式上传文件
图片地址获取图像数据
使用Glide加载网络图片
利用Glide实现图片的三级缓存
需求
界面设计
代码
若是直接将以下文件的apk发送到手机,是不能安装成功的
通过签名 jks
这里需要一个商店的key,因为没有,所以创建一个新的,点击Create new
首先指明生成的jks文件的路径,点击这个文件夹图标,个人习惯放在D盘的Apk目录下,然后命名jks的名称为Test,然后ok
然后输入这个jks和别名的一些相关信息,上方的商店密码和别名密码可以设置为一样的,这样便于记忆,只不过安全系数就降低了。我上面设置的是123456,信息都设置好了之后,点击OK。
若是出现以下弹窗,直接ok,无影响
勾选记住密码,点击next
选择release 版本,既是发布版本,然后点击Finnish ;debug为测试版本 ,我的apk位置默认是工程下的
当你看到屏幕的右下角的进度条完成了,说明你的APK已经生成了,你可以在你的app下看到多了一个release的文件夹,里面就是app-release.apk包,下面你可以复制发到手机上,然后下载,手机上是可以安装的
然后在你刚刚选择的文件下就会看到一个jks
在工程目录下,就可以找到qpk文件
若是在第 8 步时选择debug 版本 测试版本,可能会安装成功,这是为什么呢?
那个这种方式生成的包在实际开发中起到什么作用呢?
在实际开发中,如果是自己测试,那么通过就直接用usb安装在手机上,此时就是文中第一种情况。
而如果当项目要上线或者上应用市场时,就需要用jks来生成release包上传到应用市场上。
实际开发中还有一点就是你的应用自己自己测试的并不是很到位,因此产品和测试会需要让你打包给他们做进一步测试,此时你可以给通过jks生成的debug包,也可以给release包,但是通常是debug包。
从上面的操作中,知道了一些内容,而在基本的开发中也是会用到的,而为了更好区别不同的包名,常用会给apk进行一个重命名,见名知意。
修改app下的build.gradle
// 自定义打包名称
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "apk名.apk"
}
}
每次添加后记得Sync(同步)一下,然后重新打一个包,修改了生成apk的位置,如下下图所示
然后next 两次,使用之前是jks签名即可,最后这里我修改了apk位置
在Apk目录下查看你apk文件 release版本
outputFileName = "apk名_${buildType.name}.apk"
借用别人的图片:
outputFileName = "apk名_${buildType.name}_v${versionName}.apk"
再次借用别人的图片:
在android{}闭包外增加
构建年月日时分秒
def generateTime() {
return new Date().format("yyyy_MM_dd_HH_mm_ss")
}
最后打包来看看修改之后的效果
制作App图片
APK瘦身
版本设置
发布模式
数据库加密
反编译
代码混淆
第三方加固及重前面
cationChannel
桌面应用添加消息图标
服务的启动和停止
服务的绑定与 解绑
推送服务到前台
分线程通过Handler 操作界面
异步任务 AsyncTask
异步服务 IntentService
BottomNavigationView 实现底部标签栏
自定义标签栏
结合RadioGroup和ViewPager自定义底部标签栏
工具栏 Toolbar
溢出菜单OverflowMenu
标签布局TabLayout
循环视图RecycleView
布局管理器LayoutManager
动态更新循环视图
下拉刷新布局SwipeRefreshLayout
第二代翻页视图ViewPager2
给ViewPager2集成标签布局
实战项目:电商首页
需求
界面
代码
使用相机拍摄照片
从相册中选取图片
对图片进行简单加工
图像解码器ImageDecoder
使用录音机录制音频
利用MediaPlayer播放音频
利用MediaRecorder录制音频
使用摄像机录制视频
从视频库中选取视频
利用视频视图(VideoView)播放视频
需求
界面设计
实现代码
HTTP接口访问
GET方式调用HTTP接口
POST方式调用HTTP接口
通知栏显示下载进度
主动轮询当前的下载进度
利用POST方式上传文件
图片地址获取图像数据
使用Glide加载网络图片
利用Glide实现图片的三级缓存
需求
界面设计
代码
若是直接将以下文件的apk发送到手机,是不能安装成功的
[外链图片转存中…(img-MBV9mwdw-1665663924504)]
[外链图片转存中…(img-VgwT5C0V-1665663924504)]
通过签名 jks
[外链图片转存中…(img-yhaZ6bKy-1665663924505)]
[外链图片转存中…(img-rgLmGK0H-1665663924505)]
这里需要一个商店的key,因为没有,所以创建一个新的,点击Create new
[外链图片转存中…(img-IpQpBT2N-1665663924506)]
首先指明生成的jks文件的路径,点击这个文件夹图标,个人习惯放在D盘的Apk目录下,然后命名jks的名称为Test,然后ok
[外链图片转存中…(img-2LpJfzWh-1665663924507)]
然后输入这个jks和别名的一些相关信息,上方的商店密码和别名密码可以设置为一样的,这样便于记忆,只不过安全系数就降低了。我上面设置的是123456,信息都设置好了之后,点击OK。
[外链图片转存中…(img-6G0kA7hl-1665663924507)]
若是出现以下弹窗,直接ok,无影响
[外链图片转存中…(img-oTZhWTAx-1665663924508)]
勾选记住密码,点击next
[外链图片转存中…(img-gGIN9osr-1665663924509)]
选择release 版本,既是发布版本,然后点击Finnish ;debug为测试版本 ,我的apk位置默认是工程下的
[外链图片转存中…(img-ipAUl8Sx-1665663924510)]
当你看到屏幕的右下角的进度条完成了,说明你的APK已经生成了,你可以在你的app下看到多了一个release的文件夹,里面就是app-release.apk包,下面你可以复制发到手机上,然后下载,手机上是可以安装的
[外链图片转存中…(img-OoyCoiKf-1665663924510)]
然后在你刚刚选择的文件下就会看到一个jks
[外链图片转存中…(img-dnUu9ptS-1665663924511)]
在工程目录下,就可以找到qpk文件
[外链图片转存中…(img-hUZ0no91-1665663924511)]
若是在第 8 步时选择debug 版本 测试版本,可能会安装成功,这是为什么呢?
那个这种方式生成的包在实际开发中起到什么作用呢?
在实际开发中,如果是自己测试,那么通过就直接用usb安装在手机上,此时就是文中第一种情况。
而如果当项目要上线或者上应用市场时,就需要用jks来生成release包上传到应用市场上。
实际开发中还有一点就是你的应用自己自己测试的并不是很到位,因此产品和测试会需要让你打包给他们做进一步测试,此时你可以给通过jks生成的debug包,也可以给release包,但是通常是debug包。
从上面的操作中,知道了一些内容,而在基本的开发中也是会用到的,而为了更好区别不同的包名,常用会给apk进行一个重命名,见名知意。
修改app下的build.gradle
// 自定义打包名称
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "apk名.apk"
}
}
[外链图片转存中…(img-MYR0ptOG-1665663924512)]
每次添加后记得Sync(同步)一下,然后重新打一个包,修改了生成apk的位置,如下下图所示
[外链图片转存中…(img-HQ815L53-1665663924513)]
[外链图片转存中…(img-fs6PH8om-1665663924513)]
然后next 两次,使用之前是jks签名即可,最后这里我修改了apk位置
[外链图片转存中…(img-DxsmBY13-1665663924514)]
在Apk目录下查看你apk文件 release版本
[外链图片转存中…(img-1TP1pTHe-1665663924515)]
outputFileName = "apk名_${buildType.name}.apk"
[外链图片转存中…(img-AchigcqM-1665663924515)]
借用别人的图片:
[外链图片转存中…(img-JG6e1xNu-1665663924516)]
outputFileName = "apk名_${buildType.name}_v${versionName}.apk"
[外链图片转存中…(img-z0Ojd6iE-1665663924516)]
再次借用别人的图片:
[外链图片转存中…(img-hT8SOZyo-1665663924517)]
在android{}闭包外增加
构建年月日时分秒
def generateTime() {
return new Date().format("yyyy_MM_dd_HH_mm_ss")
}
[外链图片转存中…(img-q22CqTom-1665663924518)]
最后打包来看看修改之后的效果
[外链图片转存中…(img-kIUlyoR0-1665663924518)]
制作App图片
APK瘦身
版本设置
发布模式
数据库加密
反编译
代码混淆
第三方加固及重前面