封面画师:画师JW 封面ID:78042954
基本程序单元Activity
Activity概述
Activity中文含义:活动,但是在Android中Activity的含义是手机屏幕的一屏,它是Android程序中重要的基本组件之一。
Activity的4种状态
运行状态 :当安卓应用在运行时,此时就是处于运行状态。
暂停状态 :当我们在安卓应用中执行退出时,此时应用往往会弹出“确认退出”的对话框,此时就是处于暂停状态。
停止状态 :在 暂停状态 时,我们点击了“确认退出”的对话框中的“确认”按钮后,此时就是处于停止状态。
销毁状态 :当我们在系统后台将某个安卓应用进程杀掉后,此时就是处于销毁状态。
运行状态和暂停状态是可见的,其他两种是不可见的。
Activity的生命周期
矩形框内表示内容可已被回调的方法,非矩形框内表示Activity的重要状态。
创建、启动和关闭Activity
创建Activity
创建继承自Activity的Activity;
1 public class DetailActivity extends Activity {...}
重写需要的回调方法;
设置要显示的视图;
1 2 3 4 5 @ Override //通常重写onCreate()方法
protected void onCreate ( Bundle savedInstanceState) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main ) ; //设置布局
}
到这一步,Activity的基本骨架已经完成,但是这样的Activity并不能成功运行。
我们还需要配置我们定义的Activity 。
进入AndroidManifest.xml进行配置:
注意:我们在配置Activity时,需要注意我们自定义的Activity是否在该配置文件制定的包下,如果不在,需要我们书写包名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <? xml version = "1.0" encoding = "utf-8" ?>
< manifest xmlns:android = "http://schemas.android.com/apk/res/android"
package = "com.yang.activity" > <!--注意此处的package属性值-->
< application
...>
< activity android:name = ".MainActivity" >
< intent-filter >
< action android:name = "android.intent.action.MAIN" />
< category android:name = "android.intent.category.LAUNCHER" />
</ intent-filter >
</ activity >
<!--注册的Activity-->
< activity android:name = ".DetailActivity" android:label = "详细" ></ activity >
</ application >
</ manifest >
当然我们也可以使用Android Studio直接创建Activity。
启动Activity
当我们创建并配置好一个Activity后,Activity并不会直接显示在屏幕上,还需要我们启动它。
对于入口Activity,我们可以在AndroidManifest.xml进行配置: (将一个Activity配置成程序入口)
1 2 3 4 5 6 7 <!--配置一个intent过滤器-->
< intent-filter >
<!--把一个Activity设置为程序的主启动项-->
< action android:name = "android.intent.action.MAIN" />
<!--指定在什么环境下这个动作才会被响应-->
< category android:name = "android.intent.category.LAUNCHER" />
</ intent-filter >
Intent理解: 我们假设把Activity比作成一个人,那么Intent就相当于这个人的想法。可见,Activity需要通过Intent来表达自己的“意图”。我们可以在后面内容的Intent处看到其详细理解。
对于其他的Activity我们可以使用startActivity() 来启动一个Activity。
在MainActivity中启动DetailActivity:
activity_main.xml:
1 2 3 4 5 < Button
android:id = "@+id/button1"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "查看详细" />
detail_main.xml:
1 2 3 4 5 6 7 8 9 < TextView
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "详细内容" />
< Button
android:id = "@+id/Button_close"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "关闭" />
MainActivity.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
Button button = findViewById ( R . id . button1 );
button . setOnClickListener ( new View. OnClickListener () {
@ Override
public void onClick ( View v) {
//创建Intent对象,参数为上下文对象和跳转的界面的Activity的类
Intent intent = new Intent ( MainActivity . this , DetailActivity . class );
//启动Activity
startActivity (intent);
}
});
}
}
关闭Activity
直接使用finish()即可。
如果关闭的Activity不是主入口,那么关闭这个Activity就会返回到调用它的Activity中。如果是主入口的Activity,就会返回到主屏当中。
我们在DetailActivity中进行编写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class DetailActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_detail );
Button button = findViewById ( R . id . Button_close );
button . setOnClickListener ( new View. OnClickListener () {
@ Override
public void onClick ( View v) {
finish ();
}
});
}
}
实现刷新当前Activity:直接调用onCreate(null);即可
多个Activity的使用
Bundle
Bundle :一个键值对的组合,要读取时,我们只需要使用key就可以找到对应的value。
可以使用Bundle在Activity之间交换数据。
使用Bundle数据交互示意图:
模拟淘宝填写并显示收货地址的功能(数据交互)
activity_main.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout 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"
android:orientation = "vertical"
tools:context = ".MainActivity" >
< EditText
android:id = "@+id/myAddress"
android:layout_width = "match_parent"
android:layout_height = "wrap_content"
android:hint = "请输入你的地址" />
...
< Button
android:id = "@+id/btn"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:layout_marginStart = "10dp"
android:layout_marginEnd = "10dp"
android:layout_marginTop = "10dp"
android:background = "#FF5000"
android:text = "保存" />
</ LinearLayout >
activity_address.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout 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"
android:orientation = "vertical"
tools:context = ".addressActivity" >
< TextView
android:id = "@+id/address"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content" />
...
</ LinearLayout >
MainActivity.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
Button button = findViewById ( R . id . btn );
button . setOnClickListener ( new View. OnClickListener () {
@ Override
public void onClick ( View v) {
String address = ((EditText) findViewById ( R . id . myAddress )). getText (). toString ();
if ( ! "" . equals (address)){
Intent intent = new Intent ( MainActivity . this , addressActivity . class );
Bundle bundle = new Bundle ();
bundle . putCharSequence ( "address" ,address);
intent . putExtras (bundle);
startActivity (intent);
} else {
Toast . makeText ( MainActivity . this , "请填写完整的信息!" , Toast . LENGTH_SHORT ). show ();
}
}
});
}
}
AddressActivity.java:
1 2 3 4 5 6 7 8 9 10 11 12 public class addressActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_address );
Intent intent = getIntent ();
Bundle bundle = intent . getExtras ();
String address = bundle . getString ( "address" );
TextView textView_address = findViewById ( R . id . address );
textView_address . setText (address);
}
}
调用另一个Activity并返回结果
startActivityForResult();在此需要使用这个方法!
startActivityForResult()方法的基本格式如下:
1 public void startActivityForResult ( Intent intent , int requestCode)
requestCode 顾名思义是请求码的含义,需要程序员自定义 。
示例:
1 2 3 4 //创建Intent对象
Intent intent = new Intent ( MainActivity . this , DetailActivity . class ) ;
//启动新的Activity
startActivityForResult (intent , 0x007 ) ;
实现头像的切换: 需要准备6张图片,1张默认头像,5张可供选择的头像。
activity_main.xml:
1 2 3 4 5 6 7 8 9 10 11 12 < ImageView
android:id = "@+id/imageView"
android:layout_width = "100dp"
android:layout_height = "100dp"
android:layout_gravity = "center_horizontal"
android:src = "@drawable/avatar" />
< Button
android:id = "@+id/btn"
android:layout_width = "wrap_content"
android:layout_height = "40dp"
android:layout_gravity = "center_horizontal"
android:text = "选择头像" />
activuty_head.xml:
1 2 3 4 5 6 7 8 < GridView
android:id = "@+id/gridview"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:layout_marginTop = "10dp"
android:horizontalSpacing = "3dp"
android:verticalSpacing = "3dp"
android:numColumns = "4" />
MainActivity.java:
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 public class MainActivity extends Activity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
Button button = findViewById ( R . id . btn );
button . setOnClickListener ( new View. OnClickListener () {
@ Override
public void onClick ( View v) {
Intent intent = new Intent ( MainActivity . this , HeadActivity . class );
startActivityForResult (intent, 0x11 );
}
});
}
@ Override
protected void onActivityResult ( int requestCode , int resultCode , Intent data ) {
super . onActivityResult (requestCode, resultCode, data);
if (resultCode == 0x11 && resultCode == 0x11 ){
Bundle bundle = data . getExtras ();
int image = bundle . getInt ( "image" );
ImageView imageView = findViewById ( R . id . imageView );
imageView . setImageResource (image);
}
}
}
HeadActivity.java:
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 public class HeadActivity extends AppCompatActivity {
private int [] pics = new int []{ R . drawable . boy , R . drawable . boy2 , R . drawable . girl ,
R . drawable . longhairgirl , R . drawable . shorthairgirl } ;
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_head );
GridView gridView = findViewById ( R . id . gridview );
BaseAdapter adapter = new BaseAdapter () {
@ Override
public int getCount () { //注意此处记得修改
return pics . length ;
}
@ Override
public Object getItem ( int position ) { //注意此处记得修改
return position;
}
@ Override
public long getItemId ( int position ) { //注意此处记得修改
return position;
}
@ Override
public View getView ( int position , View convertView , ViewGroup parent ) {
ImageView imageView ;
if (convertView == null ) {
imageView = new ImageView ( HeadActivity . this );
imageView . setAdjustViewBounds ( true );
imageView . setMaxWidth ( 158 );
imageView . setMaxHeight ( 150 );
imageView . setPadding ( 5 , 5 , 5 , 5 );
} else {
imageView = (ImageView) convertView;
}
imageView . setImageResource (pics[position]);
return imageView;
}
};
gridView . setAdapter (adapter);
gridView . setOnItemClickListener ( new AdapterView. OnItemClickListener () {
@ Override
public void onItemClick ( AdapterView < ? > parent, View view, int position, long id) {
Intent intent = getIntent ();
Bundle bundle = new Bundle ();
bundle . putInt ( "image" ,pics[position]);
intent . putExtras (bundle);
setResult ( 0x11 ,intent);
finish ();
}
});
}
}
使用Fragment
Fragment的生命周期
通俗理解:
如果我们把Activity比作成鱼塘的水 ,那么Fragment就相当于鱼塘中的鱼 。当鱼塘干涸时,鱼也就死掉了;当Activity暂停时,里面的所有Fragment也会被暂停;当Activity被销毁时,它里面的所有Fragment也会被销毁。只有当Activity正在运行时,我们才能对里面的Fragment进行操作。
返回栈:一组Activity的集合,在这里面按照先进后出的原则(栈)放置了一系列Activity。当我们进行Fragment的转换的时候,我们可以把Fragment放入到Activity的返回栈中。
创建Fragment
extends Fragment
通过继承Fragment即可,也可以通过继承一个已经存在的Fragment子类。
新建一个布局文件:fragment_list.xml
1 2 3 4 5 6 7 8 9 <? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
android:orientation = "vertical" android:layout_width = "match_parent"
android:layout_height = "match_parent" >
< TextView
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:text = " Fragment content" />
</ LinearLayout >
在包下新建一个Java类:FragmentList.java
继承Fragment,并重写onCreateView。
1 2 3 4 5 6 7 8 public class ListFragment extends Fragment {
@ Nullable
@ Override
public View onCreateView (@ NonNull LayoutInflater inflater , @ Nullable ViewGroup container , @ Nullable Bundle savedInstanceState ) {
View view = inflater . inflate ( R . layout . fragment_list , container, false );
return view;
}
}
在Activity中添加Fragment
直接在布局文件中添加Fragment。
创建两个布局文件:fragment_list.xml,fragment_detail.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 <? 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:padding = "10dp"
android:orientation = "vertical" >
< TextView
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:textSize = "20dp"
android:text = " List Fragment" />
</ LinearLayout >
1 2 3 4 5 6 7 8 9 10 11 <? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
android:padding = "10dp"
android:layout_width = "match_parent"
android:layout_height = "match_parent" >
< TextView
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "Detail Fragment"
android:textSize = "20dp" />
</ LinearLayout >
两个布局文件对应的Activity:ListFragment.java,DetailFragment.xml 继承Fragment,并重写onCreateView。
1 2 3 4 5 6 7 8 public class ListFragment extends Fragment {
@ Nullable
@ Override
public View onCreateView (@ NonNull LayoutInflater inflater , @ Nullable ViewGroup container , @ Nullable Bundle savedInstanceState ) {
View view = inflater . inflate ( R . layout . fragment_list , container, false );
return view;
}
}
1 2 3 4 5 6 7 8 9 public class DetailFragment extends Fragment {
@ Nullable
@ Override
public View onCreateView (@ NonNull LayoutInflater inflater , @ Nullable ViewGroup container , @ Nullable Bundle savedInstanceState ) {
View view = inflater . inflate ( R . layout . fragment_detail ,container, false );
return view;
}
}
activity_main.xml:
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 <? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout 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"
android:paddingStart = "16dp"
android:paddingEnd = "16dp"
android:paddingTop = "16dp"
android:paddingBottom = "16dp"
android:orientation = "horizontal"
tools:context = ".MainActivity" >
< fragment
android:layout_width = "wrap_content"
android:layout_height = "match_parent"
android:name = "com.yang.fragment.ListFragment"
android:id = "@+id/list"
android:layout_weight = "1" />
< fragment
android:layout_width = "wrap_content"
android:layout_height = "match_parent"
android:name = "com.yang.fragment.DetailFragment"
android:id = "@+id/detail"
android:layout_weight = "2" />
</ LinearLayout >
当Activity运行时添加Fragment
利用 上述代码,不修改两个布局文件(fragment_list.xml,fragment_detail.xml)。
修改activity_main.xml:
1 2 3 4 5 6 7 8 9 10 < LinearLayout >
< TextView
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "Activity" />
< FrameLayout
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:id = "@+id/fl" ></ FrameLayout >
</ LinearLayout >
MainActivity.java:
1 2 3 4 5 6 7 8 9 10 11 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
DetailFragment detailFragment = new DetailFragment ();
FragmentTransaction fragmentTransaction = getSupportFragmentManager (). beginTransaction ();
fragmentTransaction . add ( R . id . fl ,detailFragment);
fragmentTransaction . commit ();
}
}
实现Tab标签切换功能
在本次案例中,使用Fragment实现Tab标签的切换功能。
我们需要准备三个图标资源:主页、地图、我
我们先设置三个标签对应的页面,我们使用文本框来代替:
app_fragment.xml、map_fragment.xml、mine_fragment.xml:
1 2 3 4 5 6 7 8 < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
android:layout_width = "match_parent"
android:layout_height = "match_parent" >
< TextView
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:text = "App Fragment" />
</ RelativeLayout >
1 2 3 4 5 6 7 8 9 < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
android:padding = "30dp"
android:layout_width = "match_parent"
android:layout_height = "match_parent" >
< TextView
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:text = "Map Fragment" />
</ RelativeLayout >
1 2 3 4 5 6 7 8 9 < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
android:padding = "30dp"
android:layout_width = "match_parent"
android:layout_height = "match_parent" >
< TextView
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:text = "Mine Fragment" />
</ RelativeLayout >
然后我们需要创建页面对应的Fragment:
我们在此仅贴出一个,其他两个修改inflater.inflate()的参数即可。
1 2 3 4 5 6 7 8 public class MapFragment extends Fragment {
@ Nullable
@ Override
public View onCreateView (@ NonNull LayoutInflater inflater , @ Nullable ViewGroup container , @ Nullable Bundle savedInstanceState ) {
View view = inflater . inflate ( R . layout . map_fragment , null );
return view;
}
}
在activity_app.xml中添加Fragment标记和标签栏:
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 < RelativeLayout >
< fragment
android:id = "@+id/fragment"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:name = "com.yang.tabfragment.AppFragment" />
< LinearLayout
android:layout_width = "match_parent"
android:layout_height = "50dp"
android:layout_alignParentBottom = "true"
android:orientation = "horizontal" >
< ImageView
android:id = "@+id/home"
android:layout_width = "0dp"
android:layout_height = "50dp"
android:layout_weight = "1"
android:src = "@drawable/home" />
< ImageView
android:id = "@+id/map"
android:layout_width = "0dp"
android:layout_height = "50dp"
android:layout_weight = "1"
android:src = "@drawable/map" />
< ImageView
android:id = "@+id/mine"
android:layout_width = "0dp"
android:layout_height = "50dp"
android:layout_weight = "1"
android:src = "@drawable/mine" />
</ LinearLayout >
</ RelativeLayout >
最后编写MainActivity.java:
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 public class MainActivity extends FragmentActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
ImageView home = findViewById ( R . id . home );
ImageView map = findViewById ( R . id . map );
ImageView mine = findViewById ( R . id . mine );
//设置监听器
home . setOnClickListener (l);
map . setOnClickListener (l);
mine . setOnClickListener (l);
}
View . OnClickListener l = new View . OnClickListener () {
@ Override
public void onClick ( View v) {
FragmentManager fragmentManager = getSupportFragmentManager () ;
FragmentTransaction fragmentTransaction = fragmentManager . beginTransaction ();
Fragment fragment = null ;
switch ( v . getId () ){
case R . id . home :
fragment = new AppFragment () ;
break ;
case R . id . map :
fragment = new MapFragment () ;
break ;
case R . id . mine :
fragment = new MineFragment () ;
break ;
default:break ;
}
fragmentTransaction . replace ( R . id . fragment ,fragment);
fragmentTransaction . commit ();
}
} ;
}
应用核心Intent
初始Intent
中文翻译:n. 意图、目的
概括Intent
我们在Android应用程序中需要用到三种组件:Activity 、Service 、BroadcastReceiver 。这三个组件是独立的,但他们可以相互调用、协调工作,最终组成一个完整的安卓应用。
Intent是 Android 中的一个用于组件间互相通信的信息对象,常用于启动组件和传递数据。Intent 最重要的就是其包含的信息。Intent 发出的时候,系统对应的行为正是由 Intent 所包含信息的组合 决定。一个 Intent 所包含的信息如下图:
通俗理解: 假设我们是安卓三种组件中的一种,商家是安卓三种组件中的另外一种,我们需要在商家处购买货物,货物就相当于是Bundle,而运送获取的快递员就相当于是Intent。Intent起到了信使的作用。
Intent工作过程
假设有两个Activity,分别取名为ListActivity和DetailActivity,要实现ListActivity跳转到DetailActivity:
Intent基本应用
通过Intent可以开启另一个Activity(比如用户登录成功后跳转至主页);
通过Intent可以开启一个Service(比如点击下载按钮后下载某种文件、应用);
用来传递广播。
Intent对象的属性
Intent对象的属性包括:Component name 、Action 、Data 、Category 、Extras ,Flags 。
可以参考上文中 概述Intent 中的图。
Component name
用于设置Intent对象的组件名称。通过设置Component name 可以启动其他的Activity,或者其他应用中的Activity。我们可以通过指定包名和类名来确定唯一一个Activity。
在此我们可以使用setComponent()。
activity_main.xml
1 2 3 4 5 < Button
android:id = "@+id/button"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "查看详细" />
activity_detail.xml
1 2 3 4 < TextView
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "详细内容" />
DetailActivity.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
Button button = findViewById ( R . id . button );
button . setOnClickListener ( new View. OnClickListener () {
@ Override
public void onClick ( View v) {
Intent intent = new Intent ();
ComponentName componentName = new ComponentName ( "com.yang.component_name" , "com.yang.component_name.DetailActivity" );
intent . setComponent (componentName);
startActivity (intent);
}
});
}
}
Action和Data
Action用来指定将要执行的动作,Data用来指定具体的数据。通常情况下,两者需要一起使用。
Action属性可以通过Intent定义的Action常量 来设置,具体我们可以查看SDK API文档。
常量参考链接: 点击查看1 点击查看2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ★intent action大全★
ACTION_MAIN 作为一个主要的进入口,而并不期望去接受数据
ACTION_VIEW 向用户去显示数据
ACTION_ATTACH_DATA 用于指定一些数据应该附属于一些其他的地方,例如,图片数据应该附属于联系人
ACTION_EDIT 访问已给的数据,提供明确的可编辑
ACTION_PICK 从数据中选择一个子项目,并返回你所选中的项目
ACTION_CHOOSER 显示一个activity选择器,允许用户在进程之前选择他们想要的
ACTION_GET_CONTENT 允许用户选择特殊种类的数据,并返回(特殊种类的数据:照一张相片或录一段音)
ACTION_DIAL 拨打一个指定的号码,显示一个带有号码的用户界面,允许用户去启动呼叫
ACTION_CALL 根据指定的数据执行一次呼叫(ACTION_CALL在应用中启动一次呼叫有缺陷,多数应用ACTION_DIAL,ACTION_CALL不能用在紧急呼叫上,紧急呼叫可以用ACTION_DIAL来实现)
ACTION_SEND 传递数据,被传送的数据没有指定,接收的action请求用户发数据
ACTION_SENDTO 发送一跳信息到指定的某人
ACTION_ANSWER 处理一个打进电话呼叫
ACTION_INSERT 插入一条空项目到已给的容器
ACTION_DELETE 从容器中删除已给的数据
ACTION_RUN 运行数据,运行维护
ACTION_SYNC 同步执行一个数据
ACTION_PICK_ACTIVITY 为已知的Intent选择一个Activity,返回选中的类
ACTION_SEARCH 执行一次搜索
ACTION_WEB_SEARCH 执行一次web搜索
ACTION_FACTORY_TEST 工场测试的主要进入点
Data属性是一个URI对象,通常情况下,它包括数据的URI和MIME类型,不同的Action有不同的数据规格。比如:Action字段 > Data字段
案例:使用Intent实现拨打电话和发送短信功能
activity_main.xml
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 < RelativeLayout >
< TextView
android:id = "@+id/text"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "请选择你需要的功能:" />
< ImageButton
android:id = "@+id/imageButton_phone"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:src = "@drawable/phone"
android:layout_below = "@+id/text"
android:layout_marginTop = "30dp"
android:background = "#0000"
android:scaleType = "fitXY" />
< ImageButton
android:id = "@+id/imageButton_sms"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:src = "@drawable/sms"
android:layout_toRightOf = "@+id/imageButton_phone"
android:layout_below = "@+id/text"
android:layout_marginTop = "30dp"
android:layout_marginLeft = "30dp"
android:background = "#0000"
android:scaleType = "fitXY" />
</ RelativeLayout >
MainActivity.java:
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 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
ImageButton imageButton = findViewById ( R . id . imageButton_phone );
ImageButton imageButton1 = findViewById ( R . id . imageButton_sms );
imageButton . setOnClickListener (l);
imageButton1 . setOnClickListener (l);
}
View . OnClickListener l = new View . OnClickListener () {
@ Override
public void onClick ( View v) {
Intent intent = new Intent () ;
ImageButton imageButton = (ImageButton)v ;
switch ( imageButton . getId () ){
case R . id . imageButton_phone :
intent . setAction ( intent . ACTION_DIAL );
intent . setData ( Uri . parse ( "tel:10086" ));
startActivity (intent) ;
break ;
case R . id . imageButton_sms :
intent . setAction ( intent . ACTION_SENDTO );
intent . setData ( Uri . parse ( "smsto:10086" ));
intent . putExtra ( "sms_body" , "CXLL" );
startActivity (intent) ;
break ;
}
}
} ;
}
因为要打电话和发短信,需要在AndroidManifest.xml中开启权限。
AndroidManifest.xml:
1 2 3 4 5 < manifest >
< uses-permission android:name = "android.permission.CALL_PHONE" />
< uses-permission android:name = "android.permission.SEND_SMS" />
...
</ manifest >
Action和Category
Action属性的使用方法与上一条一样。
Category属性:对执行动作的类别进行描述,在使用这个属性时,可以使用Intent提供的Category常量 来设置,具体我们可以查看SDK API文档。
参考链接:参考链接1
Category常量
常量对应字符串
简单说明
CATEGORY_DEFAULT
android.intent.category.DEFAULT
默认的Category
CATEGORY_BROWSABLE
android.intent.category.BROWSABLE
指定该Activity能被浏览器安全调用
CATEGORY_TAB
android.intent.category.TAB
指定Activity作为TabActivity的Tab页
CATEGORY_LAUNCHER
android.intent.category.LAUNCHER
Activity显示顶级程序列表中(设置程序主入口)
CATEGORY_INFO
android.intent.category.INFO
用于提供包信息
CATEGORY_HOME
android.intent.category.HOME
设置该Activity随系统启动而运行(返回系统桌面)
CATEGORY_PREFERENCE
android.intent.category.PREFERENCE
该Activity是参数面板
CATEGORY_TEST
android.intent.category.TEST
该Activity是一个测试
CATEGORY_CAR_DOCK
android.intent.category.CAR_DOCK
指定手机被插入汽车底座(硬件)时运行该Activity
CATEGORY_DESK_DOCK
android.intent.category.DESK_DOCK
指定手机被插入桌面底座(硬件)时运行该Activity
CATEGORY_CAR_MODE
android.intent.category.CAR_MODE
设置该Activity可在车载环境下使用
案例:实现单击按钮关闭界面回到系统桌面
activity_main.xml:
1 2 3 4 5 6 7 8 9 < LinearLayout >
< Button
android:id = "@+id/button1"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:layout_marginTop = "70dp"
android:layout_marginStart = "60dp"
android:text = "关闭界面" />
</ LinearLayout >
MainActivity.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
Button button = findViewById ( R . id . button1 );
button . setOnClickListener ( new View. OnClickListener () {
@ Override
public void onClick ( View v) {
Intent intent = new Intent ();
intent . setAction ( intent . ACTION_MAIN );
intent . addCategory ( intent . CATEGORY_HOME );
startActivity (intent);
}
});
}
}
Extras属性
主要用于向Intent组件中添加附加信息。通常情况下,这些附加信息由键值对的形式来保存。
在Intent对象中提供了putExtras()方法,用于把Bundle对象作为附加数据来进行添加。如果我们要获取保存在Bundle对象中的信息,可以使用getExtras()方法来获取。
Bundle对象传递数据需要数据小于0.5MB,否则会报OOM(Out Of Memory)错误。
这个属性常用于在多个Activity进行数据交换 时。
Flags属性
作用:指示安卓程序应该如何启动另一个Activity,指示程序启动以后如何处理。
在使用这个属性时,可以使用Intent提供的Flags常量 来设置,具体我们可以查看SDK API文档。
在此附上三个参考链接以供查看: 参考链接1 参考链接2 参考链接3
案例:Intent.FLAG_ACTIVITY_NO_HISTORY的使用
activity_main.xml:
1 2 3 4 5 < Button
android:id = "@+id/button"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "查看详情" />
activity_detail.xml:
1 2 3 4 < TextView
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "详情内容" />
MainActivity.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
Button button = findViewById ( R . id . button );
button . setOnClickListener ( new View. OnClickListener () {
@ Override
public void onClick ( View v) {
Intent intent = new Intent ( MainActivity . this , DetailActivity . class );
//让我们当前的Activity不在历史栈中保留
intent . setFlags ( Intent . FLAG_ACTIVITY_NO_HISTORY );
startActivity (intent);
}
});
}
}
运行结果:
如果不使用 FLAG_ACTIVITY_NO_HISTORY,当我们点击“查看详情”按钮后,界面会跳转到activity_detail界面,并显示内容:“详情内容”。此时我们不停止当前程序,然后返回至系统桌面,再在后台找到该项目并进入该项目,进去后我们会发现当前界面依旧在activity_detail界面 。
如果使用 FLAG_ACTIVITY_NO_HISTORY,当我们点击“查看详情”按钮后,界面会跳转到activity_detail界面,并显示内容:“详情内容”。此时我们不停止当前程序,然后返回至系统桌面,再在后台找到该项目并进入该项目,进去后我们会发现当前界面已经返回至程序入口界面 。
Intent种类
Intent种类分为两种:显式Intent 和隐式Intent 。
显式Intent
含义 :我们在创建Intent对象时,直接指定目标组件(Activity、Service、BroadcastReceiver)名称就可以启动目标组件。我们明确知道要启动的Activity或Service类的名称时就使用显式Intent。
创建显式Intent对象的语法格式如下:
1 Intent intent = new Intent ( Context packageContext , Class < ? > cls) ;
其中:
packageContext 为上下文对象,如:MainActivity.this
cls 为要启动的Activity的类,如:DetailActivity.class
隐式Intent
含义 :我们在创建Intent对象时,不指定目标组件,而是定义action、category或data,让Android系统根据设置好的匹配规则找到目标组件。使用隐式Intent时,我们通常需要给Intent对象定义action、category或data属性。
案例:使用隐式Intent打开哔哩哔哩
activity_main.xml:
1 2 3 4 5 < Button
android:id = "@+id/button"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "打开哔哩哔哩" />
MainActivity.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
Button button = findViewById ( R . id . button );
button . setOnClickListener ( new View. OnClickListener () {
@ Override
public void onClick ( View v) {
Intent intent = new Intent ();
intent . setAction ( intent . ACTION_VIEW );
intent . setData ( Uri . parse ( "http://www.bilibili.com" ));
startActivity (intent);
}
});
}
}
显式Intent和隐式Intent的区别:
显式Intent
隐式Intent
直接指定目标组件的名称
不会用组件名称定义要激活的目标组件
多用于在应用程序内部传递信息
多用于在不同应用之间传递信息
Intent过滤器
主要应用在使用隐式Intent来启动Activity时。
隐式启动Activity时,并没有在Intent中指明Acitivity所在的类,因此,Android系统一定存在某种匹配机制,使Android系统能够根据Intent中的数据信息,找到需要启动的Activity。这种匹配机制是依靠Android系统中的Intent过滤器(Intent Filter)来实现的。
Intent过滤器是一种根据Intent中的动作(action)、类别(category)和数据(data)等内容,对适合接收该 Intent 的组件进行匹配和筛选的机制。Intent过滤器可以匹配数据类型、路径和协议,还可以确定多个匹配项顺序的优先级(priority)。应用程序的Activity、Service和 BroadcastReceiver 组件都可以注册Intent过滤器。这样,这些组件在特定的数据格式上则可以产生相应的动作。
为了使组件能够注册Intent过滤器,通常在AndroidManifest.xml 的各个组件下定义 < intent-filter > 节点,然后再< intent-filter >节点中声明该组件所支持的动作、执行的环境和数据格式等信息。当然,也可以在程序代码中动态的为组件设置Intent过滤器。 < intent-filter >节点支持< action >标签 、 < category >标签 和 < data >标签,分别用来定义Intent过滤器的动作、类型和数据。
参考链接:参考链接
常用的标签:
在Activity中使用包含定义动作的隐式Intent启动另外一个Activity
案例概述:主入口有一张尺寸较小的图片,点击下方的“查看大图”后,弹出选择框,选择后进入页面查看大图。因此本案例需要首先准备一张图片。
activity_main.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 < RelativeLayout >
< ImageView
android:id = "@+id/image"
android:layout_width = "200dp"
android:layout_height = "200dp"
android:layout_centerInParent = "true"
android:src = "@drawable/image" />
< Button
android:id = "@+id/button"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:layout_below = "@+id/image"
android:layout_centerInParent = "true"
android:text = "查看大图" />
</ RelativeLayout >
activity_show.xml:
1 2 3 4 < ImageView
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:src = "@drawable/image" />
MainActivity.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
Button button = findViewById ( R . id . button );
button . setOnClickListener ( new View. OnClickListener () {
@ Override
public void onClick ( View v) {
Intent intent = new Intent ();
intent . setAction ( intent . ACTION_VIEW );
startActivity (intent);
}
});
}
}
最后需要进入AndroidManifest.xml文件进行配置:
1 2 3 4 5 6 < activity android:name = ".ShowActivity" >
< intent-filter >
< action android:name = "android.intent.action.VIEW" />
< category android:name = "android.intent.category.DEFAULT" />
</ intent-filter >
</ activity >
Android程序调试
DDMS工具使用
Android开发管径提供的调试工具,可以查看到指定进程运行时的线程信息、内存信息、内存分配、
为测试设备截屏、查看Log Cat日志等等。
谷歌正慢慢将DDMS抛弃!!
打开DDMS
在Android Studio 中打开;
独立打开(在SDK目录下的可执行程序,在Android 10 SDK目录下无该可执行程序)。
打开DDMS之前,我们需要在Android Studio中打开模拟器。
Android Studio升级到 3.0 以后,使用Android Profiler替代了DDMS。
输出日志信息
Log类
结构:
继承 java.lang.Object
在包 android.util.Log 中
Log类提供的方法
方法
作用
日志颜色
Log.v(String tag,String msg)
输出冗余信息
黑色 - VERBOSE
Log.d(String tag,String msg)
输出调试信息
蓝色 - DEBUG
Log.i(String tag,String msg)
输出普通信息
绿色 - INFO
Log.w(String tag,String msg)
输出警告信息
橙色 - WARN
Log.e(String tag,String msg)
输出错误信息
红色 - ERROR
日志级别从低到高。
案例:测试输出日志信息
activity_main.xml:
1 2 3 4 5 6 7 8 < RelativeLayout >
< ImageView
android:id = "@+id/image"
android:layout_width = "200dp"
android:layout_height = "200dp"
android:layout_centerInParent = "true"
android:src = "@drawable/image" />
</ RelativeLayout >
MainActivity.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class MainActivity extends AppCompatActivity {
private static String TAG = "MainActivity:" ;
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
Log . e (TAG, "MR输出的【错误信息】" );
Log . w (TAG, "MR输出的【警告信息】" );
Log . i (TAG, "MR输出的【普通信息】" );
Log . d (TAG, "MR输出的【调试信息】" );
Log . v (TAG, "MR输出的【冗余信息】" );
}
}
输出结果:
日志级别过滤器可以输出比选中级别高 的日志信息。
程序调试
使用Android Studio 编译器调试
使用Android Studio 编写代码时,使用ALT + ENTER快捷键可以快速修正代码。
其他AS使用操作与IDEA一样,毕竟AS是基于 Intellij IDEA的,在此不再累述。
使用Android Studio 调试器调试
使用断点的方式进行调试,设置好断点后,使用Debug运行。
Debug调试按钮:
Step Over: 单步跳过,运行单独的一行程序代码,但是不进入调用这个方法的内部。如果方法中有断点,则会在断点处停止。
Step Into:单步跳入,跳入到调用方法或者对象的内部单步执行程序。
Force Step Into:强制单步跳入,跳入所有被调入的方法。
Step Out:单步跳出,跳出当前执行或跳入的方法。
Run to Cursor:运行到下一个断点,如果程序中没有断点或出现异常,程序直接运行到结束。
Evaluate Expression:计算表达式。可以输入某些变量名,然后进行计算。
Variables(变量面板):
在变量面板修改某个变量的值:右击选中变量 > Set Value > 输入修改的值并回车以完成修改。
Android事件处理和手势
事件处理概述
基于监听的事件处理
做法:
为安卓的UI组件绑定特定的事件监听器。比如:为按钮绑定单击事件监听器。
三类对象:
事件监听器(Event Listener):监听事件源发生的事件,并对不用的事件作出相应的响应。
事件源(Event Source):事件产生的来源。一般情况下指各种组件,比如说窗口、按钮、菜单等。
事件(Event):封装了UI组件上发生的特定事件的具体信息。
处理流程:
基于回调的事件处理
做法:
重写Android组件特定的回调方法或重写Activity的回调方法。
回调方法:当某个事件发生时所调用的方法。
重写的方法:
通常重写 onTouchEvent()、onKeyDown()、onKeyUp()这三个方法。
重写的三种方法演示:
activity_main.xml 使用默认的即可。
MainActivity.java:
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 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
}
@ Override
public boolean onTouchEvent ( MotionEvent event ) {
Toast . makeText ( MainActivity . this , "触摸" , Toast . LENGTH_SHORT ). show ();
return super . onTouchEvent (event);
}
@ Override
public boolean onKeyDown ( int keyCode , KeyEvent event ) {
Toast . makeText ( MainActivity . this , "按下" , Toast . LENGTH_SHORT ). show ();
return super . onKeyDown (keyCode, event);
}
@ Override
public boolean onKeyUp ( int keyCode , KeyEvent event ) {
Toast . makeText ( MainActivity . this , "抬起" , Toast . LENGTH_SHORT ). show ();
return super . onKeyUp (keyCode, event);
}
}
测试方法: 运行后点击屏幕任意空白处可以测试“触摸”,调节音量可以测试“按下”和“抬起”。
区别
对于通用性 的事件采用基于回调 的事件处理方式。
对于某些特定的 事件采用基于监听 的事件处理方式。
物理按键事件处理
物理按键:手机上真实存在的按键。
按键状态:
安卓为每一个物理按键都提供了三个方法,分别是:
按下,但没有松开:onKeyDown()
抬起,松开某个按键:onKeyUp()
长按,长按某个按键:onKeyLongPress()
物理按键常量:
音量键:KEYCODE_VOLUME_UP 和 KEYCODE_VOLUME_DOWM
电源键:KEYCODE_POWER
返回键:KEYCODE_BACK
主屏键:KEYCODE_HOME
菜单键:KEYCODE_MENU
双击返回键退出应用
本质是两秒内点击两次返回键退出应用。
activity_main.xml 使用默认文件。
MainActivity.java:
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 //继承Activity不让界面显示ActionBar
public class MainActivity extends Activity {
private long exitTime = 0 ;
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
}
//1. 重写onKeyDown()方法来拦截用户单击后退按钮事件
@ Override
public boolean onKeyDown ( int keyCode , KeyEvent event ) {
if (keyCode == KeyEvent . KEYCODE_BACK ){
exit ();
return false ;
}
return super . onKeyDown (keyCode, event);
}
//2. 创建退出方法exit()
public void exit (){
//判断两次按下返回键的时间差
if (( System . currentTimeMillis () - exitTime > 2000 )){
Toast . makeText ( MainActivity . this , "再按一次退出程序" , Toast . LENGTH_SHORT ). show ();
exitTime = System . currentTimeMillis ();
} else {
finish ();
System . exit ( 0 );
}
}
}
触摸屏事件处理
单击事件
为主键设置单击事件监听器:setOnClickListener(View.OnClickListener)
setOnClickListener():
1 2 3 public static interface View .OnClickListener {
public void onClick ( View v );
}
正因如此,我们在以前为按钮设置单击事件监听器时需要重写onClick()方法。
长按事件
长按事件需要长按2秒以上才会触发 。
setOnLongClickListener(View.OnLongClickListener)为组件设置长按事件监听器。
1 2 3 public static interface View .OnLongClickListener {
public boolean onLongClick ( View v );
}
我们创建长按事件监听器时需要重写onLongClick()方法。
长按图片,弹出 收藏/举报 菜单
activity_main.xml:
1 2 3 4 5 6 7 < LinearLayout android:gravity = "center" >
< ImageView
android:id = "@+id/image"
android:layout_width = "250dp"
android:layout_height = "250dp"
android:src = "@drawable/image" />
</ LinearLayout >
MainActivity.java:
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 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
//2. 将长按事件注册到菜单中,并打开菜单
ImageView imageView = findViewById ( R . id . image );
imageView . setOnLongClickListener ( new View. OnLongClickListener () {
@ Override
public boolean onLongClick ( View v) {
registerForContextMenu (v);
openContextMenu (v);
return true ;
}
});
}
// 1. 在MainActivity中重写onCreateContextMenu菜单,为菜单添加选项值
@ Override
public void onCreateContextMenu ( ContextMenu menu ,
View v , ContextMenu . ContextMenuInfo menuInfo ) {
super . onCreateContextMenu (menu, v, menuInfo);
menu . add ( "收藏" );
menu . add ( "举报" );
}
}
触摸事件
setOntouchListener(View.OnTouchListener)为组件设置触摸事件监听器
1 2 3 public interface View .OnTouchListener {
public abstract boolean onTouch ( View v , MotionEvent event );
}
我们创建触摸事件监听器时需要重写onTouch()方法。
MotionEvent:保存发生触摸的位置、事件等细节信息。
实现帽子的拖动
我们需要先准备一个帽子的图片资源。
activity_main.xml:
1 2 3 4 5 6 7 8 < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
xmlns:app = "http://schemas.android.com/apk/res-auto"
xmlns:tools = "http://schemas.android.com/tools"
android:id = "@+id/relative"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
tools:context = ".MainActivity" >
</ RelativeLayout >
新建一个Java类,取名为HatView.java 用于指定帽子的位置和绘制帽子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // 1. 创建自定义View,绘制帽子
public class HatView extends View {
public float bitmapX ;
public float bitmapY ;
public HatView ( Context context ) {
super (context);
bitmapX = 65 ;
bitmapY = 0 ;
}
@ Override
protected void onDraw ( Canvas canvas ) {
super . onDraw (canvas);
Paint paint = new Paint ();
Bitmap bitmap = BitmapFactory . decodeResource ( this . getResources (),
R . drawable . hat );
canvas . drawBitmap (bitmap,bitmapX,bitmapY,paint);
if ( ! bitmap . isRecycled ()){
bitmap . recycle ();
}
}
}
MainActivity.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
// 2. 创建并实例化帽子类对象,并未帽子添加触摸事件监听器,在重写的触摸方法中根据触摸的位置重绘帽子
final HatView hat = new HatView ( MainActivity . this );
hat . setOnTouchListener ( new View. OnTouchListener () {
@ Override
public boolean onTouch ( View v, MotionEvent event) {
hat . bitmapX = event . getX () - 80 ;
hat . bitmapY = event . getY () - 80 ;
hat . invalidate ();
return true ;
}
});
// 3. 把帽子添加到布局管理器中
RelativeLayout rl = findViewById ( R . id . relative );
rl . addView (hat);
}
}
单击事件和触摸事件的区别
安卓应用在执行的时候会先触发触摸事件 ,如果触摸事件没有完全消费掉事件,再触发单击事件 。
消费事件 :一次UI操作是否完成响应了,如果在重写的方法当中返回了true,那么就表示完全消费了这个事件;如果返回的是false,那么它会再交给后面的事件进行处理。
单击事件与触摸事件的区别案例
activity_main.xml:
1 2 3 4 5 < Button
android:id = "@+id/btn"
android:layout_width = "match_parent"
android:layout_height = "50dp"
android:text = "单击与触摸的区别" />
MainActivity.java:
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 public class MainActivity extends AppCompatActivity {
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
Button button = findViewById ( R . id . btn );
//1. 为按钮添加单击事件监听器
button . setOnClickListener ( new View. OnClickListener () {
@ Override
public void onClick ( View v) {
Log . i ( "onClick" , "单击事件" );
}
});
//2. 为按钮添加触摸事件监听器
button . setOnTouchListener ( new View. OnTouchListener () {
@ Override
public boolean onTouch ( View v, MotionEvent event) {
if ( event . getAction () == MotionEvent . ACTION_DOWN ){
Log . i ( "onTouch" , "按下" );
} else if ( event . getAction () == MotionEvent . ACTION_UP ){
Log . i ( "onTouch" , "抬起" );
}
return true ;
}
});
}
}
编写好后,我们启动模拟器运行程序,打开Logcat观察日志输出结果:
此时重写的触摸事件方法返回值为false ,点击按钮,日志输出结果为:
如果将重写的触摸事件方法返回值改为true ,那么日志输出结果为:
结论:
单击事件触发一个动作,触摸事件触发两个动作。
安卓应用在执行的时候会先触发触摸事件,如果触摸事件没有完全消费掉事件,再触发单击事件。
手势检测
安卓为手势检测提供了GestureDetector类。
我们在创建这个类的对象的时候需要传入GestureDetector.OnGestureListener接口的实例,这个接口是个监听器,用来对用户的手势进行响应。
我们在实现OnGestureListener实例的时候,必须重写六个方法:
方法名
含义
onDown()
触摸事件按下时触发
onFling()
当用户的手指在屏幕上滑过一段距离时触发
onLongPress()
当用户的手指在屏幕上长按时触发
onScroll()
当用户的手指在屏幕上滑动时触发
onShowPress()
当用户的手指在屏幕上还没有滑动时触发
onSingleTapUp()
当用户的手指在屏幕上轻击时触发
实现滑动图片以切换图片
**准备:**我们需要准备四张图片,取名为image1 ~ image4,并将它们放置于drawable目录下。然后编写动画文件,并放置于res/anim下(anim需要自行创建)。
动画文件:
向左推进:push_left_in.xml
1 2 3 4 5 6 7 <? xml version = "1.0" encoding = "utf-8" ?>
< set xmlns:android = "http://schemas.android.com/apk/res/android" >
< translate android:fromXDelta = "100%p" android:toXDelta = "0"
android:duration = "500" />
< alpha android:fromAlpha = "0.1" android:toAlpha = "1.0"
android:duration = "500" />
</ set >
向左推出:push_left_out.xml
1 2 3 4 5 6 7 <? xml version = "1.0" encoding = "utf-8" ?>
< set xmlns:android = "http://schemas.android.com/apk/res/android" >
< translate android:fromXDelta = "0" android:toXDelta = "-100%p"
android:duration = "500" />
< alpha android:fromAlpha = "1.0" android:toAlpha = "0.1"
android:duration = "500" />
</ set >
向右推进:push_right_in.xml
1 2 3 4 5 6 7 <? xml version = "1.0" encoding = "utf-8" ?>
< set xmlns:android = "http://schemas.android.com/apk/res/android" >
< translate android:fromXDelta = "-100%p" android:toXDelta = "0"
android:duration = "500" />
< alpha android:fromAlpha = "0.1" android:toAlpha = "1.0"
android:duration = "500" />
</ set >
向右推出:push_right_out.xml
1 2 3 4 5 6 7 <? xml version = "1.0" encoding = "utf-8" ?>
< set xmlns:android = "http://schemas.android.com/apk/res/android" >
< translate android:fromXDelta = "0" android:toXDelta = "100%p"
android:duration = "500" />
< alpha android:fromAlpha = "1.0" android:toAlpha = "0.1"
android:duration = "500" />
</ set >
activity_main.xml:
1 2 3 4 < ViewFlipper
android:id = "@+id/flipper"
android:layout_width = "match_parent"
android:layout_height = "match_parent" ></ ViewFlipper >
ViewFlipper 使用动画来控制多个组件之间的切换效果的。
MainActivity.java:
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 //1. 让MainActivity实现GestureDetector.OnGestureListener接口,并实现其所有方法
public class MainActivity extends AppCompatActivity implements GestureDetector . OnGestureListener {
Animation [] animations = new Animation [ 4 ] ;
final int distance = 50 ;
private int [] images = new int []{ R . drawable . image1 , R . drawable . image2 ,
R . drawable . image3 , R . drawable . image4 } ;
ViewFlipper flipper ;
// 2. 定义一个全局的手势检测器
GestureDetector detector ;
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
detector = new GestureDetector ( MainActivity . this , this );
//3. 将要显示的图片加载到ViewFlipper中,并初始化动画数组
flipper = findViewById ( R . id . flipper );
for ( int i = 0 ; i < images . length ; i++) {
ImageView imageView = new ImageView ( this );
imageView . setImageResource (images[i]);
flipper . addView (imageView);
}
animations[ 0 ] = AnimationUtils . loadAnimation ( this , R . anim . push_right_in );
animations[ 1 ] = AnimationUtils . loadAnimation ( this , R . anim . push_right_out );
animations[ 2 ] = AnimationUtils . loadAnimation ( this , R . anim . push_left_in );
animations[ 3 ] = AnimationUtils . loadAnimation ( this , R . anim . push_left_out );
}
@ Override
public boolean onDown ( MotionEvent e ) { return false ;}
@ Override
public void onShowPress ( MotionEvent e ) { }
@ Override
public boolean onSingleTapUp ( MotionEvent e ) { return false ;}
@ Override
public boolean onScroll ( MotionEvent e1 , MotionEvent e2 ,
float distanceX , float distanceY ) { return false ;}
@ Override
public void onLongPress ( MotionEvent e ) { }
@ Override
public boolean onFling ( MotionEvent e1 , MotionEvet e2 , float velocityX , float velocityY ) {
//4. 在onFling()中通过触摸事件的X坐标判断是向左滑动还是向右滑动,并为其设置动画
//从右向左滑
if ( e1 . getX () - e2 . getX () > distance){
flipper . setInAnimation (animations[ 2 ]);
flipper . setOutAnimation (animations[ 3 ]);
flipper . showPrevious ();
return true ;
} else if ( e2 . getX () - e1 . getX () > distance){
flipper . setInAnimation (animations[ 0 ]);
flipper . setOutAnimation (animations[ 1 ]);
flipper . showNext ();
return true ;
}
return false ;
}
//5. 将Activity上的触摸事件交给GestureDetector处理
@ Override
public boolean onTouchEvent ( MotionEvent event ) {
return detector . onTouchEvent (event);
}
}
手势的添加
我们可以自定义我们的手势,就像手写输入法可以识别我们手写的文字一样。
我们需要先创建手势文件,手势文件的创建需要用到一个APP:GestureBuilder 。该软件可以在谷歌应用商店安装。(当然,进谷歌应用商店需要绿色上网,或者你也可以联系本文作者获取软件)
该软件在低版本的Android Studio中是内置的,但高版本似乎取消了这个软件。
注意:GestureBuilder需要安装在我们自己的手机上,不是Android Studio的虚拟机上。
GestureBuilder的使用很简单,在此就不再描述其使用方法。
手势文件的位置
我们用GestureBuilder创建好我们的手势文件后,我们需要获取我们的手势文件,该手势文件的位置在:
1 /storage/emulated/0/Android/data/pack.GestureApp/files/gesture.txt
其中,gesture.txt为手势文件的默认名称,你也可以将其修改,在这里我选择使用默认名称。
如果你成功找到手势文件了,需要将这个手势文件发送至你的电脑。
手势文件的位置
我们将我们的手势文件放置res/raw目录下。
Android Studio的res目录下没有默认创建raw目录,因此需要我们手动创建。
到此,我们已经成功创建好了手势文件,并将其放置于指定的目录当中了。
手势的识别
通过手势的添加后,我们已经拥有了我们的手势文件,并放置在了指定的目录。
接下来我们可以通过一个示例来完成手势的识别。
activity_main.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
xmlns:app = "http://schemas.android.com/apk/res-auto"
xmlns:tools = "http://schemas.android.com/tools"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
tools:context = ".MainActivity" >
<!--1.在布局文件中添加一个编辑框和手势组件-->
< EditText
android:id = "@+id/editText"
android:layout_width = "200dp"
android:layout_height = "wrap_content"
android:layout_marginLeft = "40dp"
android:layout_marginTop = "190dp" />
< android.gesture.GestureOverlayView
android:id = "@+id/gesture"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:layout_alignParentBottom = "true"
android:gestureStrokeType = "multiple" >
</ android.gesture.GestureOverlayView >
</ RelativeLayout >
MainActivity.java:
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 //2. 实现GestureOverlayView.OnGesturePerformedListener接口,并重写onGesturePerformed()方法
public class MainActivity extends Activity implements GestureOverlayView . OnGesturePerformedListener {
private GestureLibrary library ;
private EditText editText ;
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_main );
//3.加载raw文件夹中的手势文件,如果失败退出应用
library = GestureLibraries . fromRawResource ( MainActivity . this , R . raw . gesture );
editText = findViewById ( R . id . editText );
if ( ! library . load ()) {
finish ();
}
//4.获得GestureOverlayView组件,并且为其设置属性值和事件监听器
GestureOverlayView gestureOverlayView = findViewById ( R . id . gesture );
//设置手势颜色
gestureOverlayView . setGestureColor ( Color . BLACK );
//设置淡出屏幕的间隔事件 1000ms
gestureOverlayView . setFadeOffset ( 1000 );
//添加事件监听器
gestureOverlayView . addOnGesturePerformedListener ( this );
}
@ Override
public void onGesturePerformed ( GestureOverlayView overlay , Gesture gesture ) {
//5. 获取最佳匹配进行显示,并更新编辑框
//获取全部预测结果
ArrayList < Prediction > gestures = library . recognize (gesture);
//保存当前预测的索引号
int index = 0 ;
//保存当前预测的得分
double score = 0.0 ;
//获得最佳匹配结果
for ( int i = 0 ; i < gestures . size (); i++) {
//获取预测结果
Prediction result = gestures . get (i);
if ( result . score > score){
index = i;
score = result . score ;
}
}
//更新编辑框内容
//获取编辑框原有内容
String text = editText . getText (). toString ();
//在原有内容上追加最佳匹配结果
text += gestures . get (index). name ;
//更新编辑框
editText . setText (text);
//设置光标置于文字末尾
editText . setSelection ( text . length ());
}
}
Android核心与事件处理完