在上面我们写的ListViewDemo中,如果当我们一时手滑按了返回键,则小程序将会直接退出到模拟器主界面,那么我们需要一个退出程序对话框来提示用户是否真正要退出。在上节我们学了如何使用5中常见的对话框,但是有时候业务需求不同,为了有个漂亮的外表对话框,需要自定义对话框。
一、实现程序确认框的步骤
1.重写主Activity中的onBackPress()方法;
2.去除原有的父类的方法即super.onBackPressed();
3.编写确认对话框;
4.在确定事件里调用finish()来结束程序。
5.具体代码如下:
package com.oldtogether.adapterdemo2;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
public class MainActivity4 extends ActionBarActivity implements OnItemClickListener, OnItemLongClickListener {
// 创建数组,采用for循环进行遍历
private int[] imageIds = new int[] { R.drawable.daxiang, R.drawable.maozi, R.drawable.nangua, R.drawable.nanguobq,
R.drawable.tiaopi, R.drawable.xiaolian, R.drawable.xin, R.drawable.weixin, R.drawable.hongx,
R.drawable.xm };
private String[] titles = new String[] { "卖萌大象", "圣诞帽子", "愤怒南瓜", "难过表情", "调皮表情", "笑脸表情", "oldtogether爱心", "微信",
"粉色少女心", "一代撸" };
private String[] versions = new String[] { "版本:1.10", "版本:2.10", "版本:1.11", "版本:2.12", "版本:3.10", "版本:2.20",
"版本:5.20", "版本:1.23", "版本:2.21", "版本:2.40", };
private String[] sizes = new String[] { "大小:32.01M", "大小:32.02M", "大小:23.01M", "大小:11.01M", "大小:41.01M",
"大小:11.01M", "大小:33.01M", "大小:33.01M", "大小:33.01M", "大小:33.01M", };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listview);
// 1、过的ListView对象
ListView lv = (ListView) findViewById(R.id.lv_play);
// 2、数据源
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
for (int i = 0; i < titles.length; i++) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("logo", imageIds[i]);
map.put("title", titles[i]);
map.put("version", versions[i]);
map.put("size", sizes[i]);
list.add(map);
}
// 3、设置适配器
MyAdapter3 adapter3 = new MyAdapter3(this);
adapter3.setList(list);// 传入数据
// 4、关联适配器
lv.setAdapter(adapter3);
// 5、为ListView设置事件监听器(一下两个常用事件),让主MainActivity2实现接口,并重写方法
lv.setOnItemClickListener(this);
lv.setOnItemLongClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
/* 创建了一个意图对象,调用setClass方法表明“在那个Activity中调用那个Activity”次意图*/
Intent intent = new Intent();
intent.setClass(getApplicationContext(), DetailActivity.class);
//获得点击行的数据
HashMap <String,Object> itemMap= (HashMap<String,Object>) parent.getItemAtPosition(position);
intent.putExtra("index", ""+position);//获得编号
intent.putExtra("title", ""+itemMap.get("title"));//获得标题
startActivity(intent);
}
ArrayList<Integer> choice = new ArrayList<>();
@Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
//长按之多选框
final String [] interests={"看书","打篮球","大lol","听音乐","看电影","跑步"};//设置为final(常量)的原因是可以穿透作用域
new AlertDialog.Builder(this)
.setIcon(R.drawable.ic_launcher)
.setTitle("你的爱好是什么?")
.setMultiChoiceItems(interests,
new boolean []{false,false,false,false,false,false},
new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
//选中则添加,后悔是时还可以remove掉
if(isChecked){
choice.add(which);
}else{
choice.remove(which);
}
Toast.makeText(MainActivity4.this, "您当前的选择是:"+choice.toString(), Toast.LENGTH_LONG).show();
}
})
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity4.this, "您最终的决定是:"+choice.toString(), Toast.LENGTH_LONG).show();
}
})
.setNegativeButton("取消", null)
.show();
return true;
}
@Override
public void onBackPressed() {
new AlertDialog.Builder(this)
.setIcon(R.drawable.ic_launcher)
.setTitle("确认对话框")
.setMessage("您真的要退出次程序吗?")
.setPositiveButton("取消", null)
.setNegativeButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
}).show();
}
}
5.运行结果
二、上面的很多Test都是在ListView中进行的,但是我们可以发现,Item项中的Button没有任何操作,只是简单的设置了背景转换器,那么如何让其生效呢??
1.在回到MyAdapter3.java,回顾ListView优化,在渲染每一行时,同样将Button对象获得,代码如下:
package com.oldtogether.adapterdemo2;
import java.util.List;
import java.util.Map;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
public class MyAdapter3 extends BaseAdapter {
List<Map<String, Object>> list;
LayoutInflater Inflater;// 反射器
// 初始化反射器
public MyAdapter3(Context context) {
this.Inflater = LayoutInflater.from(context);
}
public void setList(List<Map<String, Object>> list) {
this.list = list;
}
@Override
public int getCount() {
// 获取ListView的行数
return list.size();
}
@Override
public Object getItem(int index) {
// 获得Item项的对象,其中的参数为此Item的下标,从零开始
return list.get(index);
}
@Override
public long getItemId(int id) {
// 获得ItemId
return id;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
// 判断convertView是否为空,其来自getView方法的形参
if (convertView == null) {
// 通过反射获得行布局对象
convertView = Inflater.inflate(R.layout.item, null);
holder = new ViewHolder();
// 获得控件对象,并作为ViewHolder的属性
holder.logo = (ImageView) convertView.findViewById(R.id.iv_logo);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.version = (TextView) convertView.findViewById(R.id.version);
holder.size = (TextView) convertView.findViewById(R.id.size);
holder.btn = (Button) convertView.findViewById(R.id.btn);
// 将ViewHolder和convertView关联起来
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// 添加数据,注意需要强转
Map map = list.get(position);
holder.logo.setImageResource((int) map.get("logo"));
holder.title.setText((String) map.get("title"));
holder.version.setText((String) map.get("version"));
holder.size.setText((String) map.get("size"));
holder.btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("zwb","点击了Item中的安装按钮"+position);//这里牵扯到左右域的问题,应该在getView方法中的第一个参数中用final来修饰
}
});
return convertView;
}
// 自定义的ViewHolder类
public class ViewHolder {
ImageView logo;
TextView title;
TextView version;
TextView size;
Button btn;
}
}
2.运行结果
三、降低依赖,降低耦合(接口隔离原则)
1.通过上面的运行结果可以看出,我们通过Log日志达到了需求,但是不能使用Toast来弹出提示信息,因为在Context上下文时,使用匿名内部类遇见了麻烦,同时资源的利用也被局限了,在程序设计时,为了便于程序的维护,为了降低类和类之间的耦合,聪明的猿们将所用到的方法封装在一个接口中,让所需要的类来实现即可。
2.首先定义一个Interface,只是简单的封装一个butClick方法,代码如下:
package com.oldtogether.adapterdemo2;
public interface IControl {
void btnClick(int pos);
}
3.在自定义的MyAdapater3中,设置Icontrol接口的成员变量,并设置set方法,方便其他类调用;在ViewHolder中加入按钮对象并通过findViewById()此反射获得,和ConvertView绑在一起;最后在按钮点击事件中,将每一项作为参数传入到接口中的btnClick()方法中,代码如下:
package com.oldtogether.adapterdemo2;
import java.util.List;
import java.util.Map;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
public class MyAdapter3 extends BaseAdapter {
List<Map<String, Object>> list;
LayoutInflater Inflater;// 反射器
IControl control;//声明一个接口类型的成员变量
//并将其作为参数传进来,方便在其他类中调用
public void setControl(IControl control) {
this.control = control;
}
// 初始化反射器
public MyAdapter3(Context context) {
this.Inflater = LayoutInflater.from(context);
}
public void setList(List<Map<String, Object>> list) {
this.list = list;
}
@Override
public int getCount() {
// 获取ListView的行数
return list.size();
}
@Override
public Object getItem(int index) {
// 获得Item项的对象,其中的参数为此Item的下标,从零开始
return list.get(index);
}
@Override
public long getItemId(int id) {
// 获得ItemId
return id;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
// 判断convertView是否为空,其来自getView方法的形参
if (convertView == null) {
// 通过反射获得行布局对象
convertView = Inflater.inflate(R.layout.item, null);
holder = new ViewHolder();
// 获得控件对象,并作为ViewHolder的属性
holder.logo = (ImageView) convertView.findViewById(R.id.iv_logo);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.version = (TextView) convertView.findViewById(R.id.version);
holder.size = (TextView) convertView.findViewById(R.id.size);
holder.btn = (Button) convertView.findViewById(R.id.btn);
// 将ViewHolder和convertView关联起来
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// 添加数据,注意需要强转
Map map = list.get(position);
holder.logo.setImageResource((int) map.get("logo"));
holder.title.setText((String) map.get("title"));
holder.version.setText((String) map.get("version"));
holder.size.setText((String) map.get("size"));
holder.btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
control.btnClick(position);//这里只是将position(每一项传进去),具体的实现才做甩到接口中去
}
});
return convertView;
}
// 自定义的ViewHolder类
public class ViewHolder {
ImageView logo;
TextView title;
TextView version;
TextView size;
Button btn;
}
}
4.让MainActivity4.java实现我们自定义的接口IControl并重新抽象方法,当然这里用简单的Toast来实现简单的代码,这里只是提供一种框架,可以填充更加复杂的逻辑代码。具体代码如下:
package com.oldtogether.adapterdemo2;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
public class MainActivity4 extends ActionBarActivity implements OnItemClickListener, OnItemLongClickListener,IControl {
// 创建数组,采用for循环进行遍历
private int[] imageIds = new int[] { R.drawable.daxiang, R.drawable.maozi, R.drawable.nangua, R.drawable.nanguobq,
R.drawable.tiaopi, R.drawable.xiaolian, R.drawable.xin, R.drawable.weixin, R.drawable.hongx,
R.drawable.xm };
private String[] titles = new String[] { "卖萌大象", "圣诞帽子", "愤怒南瓜", "难过表情", "调皮表情", "笑脸表情", "oldtogether爱心", "微信",
"粉色少女心", "一代撸" };
private String[] versions = new String[] { "版本:1.10", "版本:2.10", "版本:1.11", "版本:2.12", "版本:3.10", "版本:2.20",
"版本:5.20", "版本:1.23", "版本:2.21", "版本:2.40", };
private String[] sizes = new String[] { "大小:32.01M", "大小:32.02M", "大小:23.01M", "大小:11.01M", "大小:41.01M",
"大小:11.01M", "大小:33.01M", "大小:33.01M", "大小:33.01M", "大小:33.01M", };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listview);
// 1、获得ListView对象
ListView lv = (ListView) findViewById(R.id.lv_play);
// 2、数据源
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
for (int i = 0; i < titles.length; i++) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("logo", imageIds[i]);
map.put("title", titles[i]);
map.put("version", versions[i]);
map.put("size", sizes[i]);
list.add(map);
}
// 3、设置适配器
MyAdapter3 adapter3 = new MyAdapter3(this);
adapter3.setList(list);// 传入数据
adapter3.setControl(this);//传入IControl的成员变量,即在此引用
// 4、关联适配器
lv.setAdapter(adapter3);
// 5、为ListView设置事件监听器(一下两个常用事件),让主MainActivity2实现接口,并重写方法
lv.setOnItemClickListener(this);
lv.setOnItemLongClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
/* 创建了一个意图对象,调用setClass方法表明“在那个Activity中调用那个Activity”次意图*/
Intent intent = new Intent();
intent.setClass(getApplicationContext(), DetailActivity.class);
//获得点击行的数据
HashMap <String,Object> itemMap= (HashMap<String,Object>) parent.getItemAtPosition(position);
intent.putExtra("index", ""+position);//获得编号
intent.putExtra("title", ""+itemMap.get("title"));//获得标题
startActivity(intent);
}
ArrayList<Integer> choice = new ArrayList<>();//创建一个ArrayList用来保存在Dialog中所选择的选项,同理设置为全局变量为了穿透作用域
@Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
//长按之多选框
final String [] interests={"看书","打篮球","大lol","听音乐","看电影","跑步"};//设置为final(常量)的原因是可以穿透作用域
new AlertDialog.Builder(this)
.setIcon(R.drawable.ic_launcher)
.setTitle("你的爱好是什么?")
/*
* setMultiChoiceItems()方法中的三个参数
* 第一个参数:资源数组;
* 第二个参数:默认情况下的初始值,这里因为是多选,所以设置为boolean类型的数组;
* 第三个参数:多选框的点击事件;
*
*/
.setMultiChoiceItems(interests,
new boolean []{false,false,false,false,false,false},
new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
/*
* 第一个参数:dialog对象;
* 第二个参数:多选对话框中用户所选中的项;
* 第三个参数:是否选中,true为选中,false为没有选中
*
*/
//选中则添加,后悔是时还可以remove掉
if(isChecked){
choice.add(which);
}else{
choice.remove(which);
}
Toast.makeText(MainActivity4.this, "您当前的选择是:"+choice.toString(), Toast.LENGTH_LONG).show();
}
})
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity4.this, "您最终的决定是:"+choice.toString(), Toast.LENGTH_LONG).show();
}
})
.setNegativeButton("取消", null)
.show();
return true;
}
@Override
public void onBackPressed() {
new AlertDialog.Builder(this)
.setIcon(R.drawable.ic_launcher)
.setTitle("确认对话框")
.setMessage("您真的要退出次程序吗?")
.setPositiveButton("取消", null)
.setNegativeButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
//System.exit(0);同理也可以退出
}
}).show();
}
//在适配器中只是调用方法,然后进行传参,在此类中实现接口并重写方法
@Override
public void btnClick(int pos) {
Toast.makeText(this, "点击了item项中的安装按钮"+pos, Toast.LENGTH_LONG).show();
}
}
5.运行结果
四、自定义对话框
1.上面学的对话框都是系统提供的,有时候当我们看见一个漂亮的对话框时,有没有像是看见一个漂亮de美眉,有追的冲动。所以我们应该学会自定义自己风格的对话框。
2.自定义对话框的步骤
第一步:自定义.xml布局文件;
第二步:获取LayoutLInflater对象;
第三步:调用inflate()方法获取View对象;
第四步:调用Builder对象的setView()方法设置View;
第五步:获取输入内容或者监听点击事件。
3.定义一个activity_main.xml,包含垂直在父容器顶部的按钮。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
tools:context="com.oldtogether.defineddialog.MainActivity" >
<Button
android:id="@+id/btn_open"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:text="对话框"
android:textColor="#eee"
android:textSize="13sp"
android:background="@drawable/btn_selector"
android:layout_centerInParent="true"
android:layout_alignParentTop="true"
android:onClick="btnClick"/>
</RelativeLayout>
2.这里贴出btn_selector.xml(在drawable下自定义的xml文件)重新温习一下前面怎么用PhotoShop和draw9patch.bat制作一个高大尚的可拉伸的圆角按钮。代码如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 不按下前的按钮 -->
<item android:drawable="@drawable/btn_gn_n" android:state_pressed="false"/>
<!-- 按下后的的按钮 -->
<item android:drawable="@drawable/btn_h_h" android:state_pressed="true"/>
<!-- 然后在item.xml中的 android:background="@drawable/btn_selector"此项设置 -->
</selector>
3.自定义的对话框,简单的来讲就是一个布局管理器,define_dialog.xml文件代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="32dp"
android:orientation="vertical"
android:background="#003">
<TextView
android:text="提醒"
android:textSize="18sp"
android:textColor="#fff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingTop="5dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:background="#87CEFA"
android:padding="10dp"
>
<TextView
android:text="恭喜您,验证码发送成功!"
android:textSize="16sp"
android:textColor="#191970"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"/>
<Button
android:id="@+id/btn_sure"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:background="@drawable/btn_selector"
android:text="确定"
android:textColor="#eee"
android:textSize="13sp"/>
</LinearLayout>
</LinearLayout>
4.将此布局加载到一个MainActivity.java文件中,并使用绑定标签de方法添加事件监听器。
package com.oldtogether.defineddialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//采用在.xml定义标签的方法来添加时间监听器,其中此方法的参数表示Button对象
Dialog dialog;//声明一个Dialog父类对象,方便调用dismiss()方法同时避免空指针
public void btnClick(View v){
LayoutInflater inflater = LayoutInflater.from(this);//通过静态方法获得反射器对象
View myView = inflater.inflate(R.layout.define_dialog, null);//通过获的反射器获得自定义的视图对象
//把自定义的视图对象,设置到对话框模板下(这样便改变了系统自带大题固定形式的对话框)
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(myView);
//给我们自定义的btn_sure添加事件监听器(这里打破经典的传统方法)
/*
* 经典的传统方法
* 1.Button btn;
* 2.btn = (Button) findViewByid(R.id.btn_sure);
* 3.btn.setOnClickListner().......
*/
myView.findViewById(R.id.btn_sure).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();//点击确实按钮的同时关闭按钮,疑惑解答:在dismiss时一般我们会觉得出现空指针,其实在点击事件发生之前对话框已经创建了,因此不会出现空指针
Toast.makeText(MainActivity.this, "点击了确定", Toast.LENGTH_LONG).show();
}
});
dialog = builder.create();//Dialog是AlertDialog的一个父类
dialog.show();
}
}
5.运行结果: