疯狂JAVA第三章笔记

来源:互联网 发布:淘宝店头像图片 编辑:程序博客网 时间:2024/04/28 20:32

3.2 基于监听的处理模型

bn.setOnClickListener(new MyClickListener()); // ①
// 定义一个单击事件的监听器
class MyClickListener implements View.OnClickListener
{
// 实现监听器类必须实现的方法,该方法将会作为事件处理器
@Override
public void onClick(View v)
{
EditText txt = (EditText) findViewById(R.id.txt);
txt.setText("bn按钮被单击了!");
}
}
基于监听事件处理模型的编程步骤:
1.获取界面组件
2.实现事件监听器类,该监听器类是一个特殊的Java类,因此要实现一个XXXlistener接口
3.调用事件源的setXXXlistener方法将事件监听器对象注册被普通组件。

定义一个飞机类
public class PlaneView extends View
{
public float currentX;
public float currentY;
Bitmap plane;
// 定义、并创建画笔
Paint p = new Paint();
public PlaneView(Context context)
{
super(context);
// 定义飞机图片
plane = BitmapFactory.decodeResource(context.getResources(),//获取图片
R.drawable.plane);
setFocusable(true);
}


@Override
public void onDraw(Canvas canvas)
{
super.onDraw(canvas);
// 绘制飞机
canvas.drawBitmap(plane, currentX, currentY, p);
}
}

在主程序中
final PlaneView planeView = new PlaneView(this);
setContentView(planeView);//创建组件设置布局为
当事件类型多
planeView.setOnKeyListener(new OnKeyListener()
{
@Override
public boolean onKey(View source, int keyCode, KeyEvent event)
{
// 获取由哪个键触发的事件
switch (event.getKeyCode())
{
// 控制飞机下移
case KeyEvent.KEYCODE_S:
planeView.currentY += speed;
break;
// 控制飞机上移
case KeyEvent.KEYCODE_W:
planeView.currentY -= speed;
break;
// 控制飞机左移
case KeyEvent.KEYCODE_A:
planeView.currentX -= speed;
break;
// 控制飞机右移
case KeyEvent.KEYCODE_D:
planeView.currentX += speed;
break;
}
// 通知planeView组件重绘
planeView.invalidate();
return true;
}
});

View一般包含如下几个内部接口:
view.onclickListener
view.oncreatcontextmenulistener:创建上下文菜单事件的时间监听器必须实现的接口
View.onFocusChangeListener:焦点改变事件的时间监听器必须实现的接口。
view.onKeyListener:按键事件的事件监听器。
view.onlongclicklistener:长按事件监听器。
view .ontouchlistener:触摸屏事件~~
所谓的时间监听器,其实就是实现了特定接口的Java类实现事件监听器通常有如下几种形式:
1.内部类形式:将监听类定义为当前类内部类
2.外部类形式:将监听类定义为外部类
3.Acitivity本身作为时间监听类:让activity本身实现监听器接口。并实现事件处理方法。
4.匿名内部类
前面的例子都是内部类

如果某个时间监听器需要被多个GUI界面共享,而且主要是完成某种业务逻辑,则可以考虑使用外部类的形式来定义时间监听器。在外部定义监听类主程序中只需要这样调用。
bn.setOnLongClickListener(new SendSmsListener(
this , address, content));

外部类创建方法
public class SendSmsListener implements OnLongClickListener
{
private Activity act;
private EditText address;
private EditText content;


public SendSmsListener(Activity act, EditText address
, EditText content)
{
this.act = act;
this.address = address;
this.content = content;
}


@Override
public boolean onLongClick(View source)
{
String addressStr = address.getText().toString();
String contentStr = content.getText().toString();
// 获取短信管理器
SmsManager smsManager = SmsManager.getDefault();
// 创建发送短信的PendingIntent
PendingIntent sentIntent = PendingIntent.getBroadcast(act
, 0, new Intent(), 0);
// 发送文本短信
smsManager.sendTextMessage(addressStr, null, contentStr
, sentIntent, null);
Toast.makeText(act, "短信发送完成", Toast.LENGTH_LONG).show();
return false;
}
}


activity本身作为监听器:

// 直接使用Activity作为事件监听器
在oncreat内部
bn.setOnClickListener(this);//添加监听器

public void onClick(View v)
{
show.setText("bn按钮被单击了!");
}
在activity内部直接定义。

匿名内部类:
bn.setOnClickListener(new OnClickListener()
{
// 实现事件处理方法
@Override
public void onClick(View v)
{
show.setText("bn按钮被单击了!");
}
});
直接在类内部创建一个事件监听对象,NEW监听器接口或事件适配器,就是这种形式。

直接绑定到标签:直接在布局文件中为指定标签绑定事件处理方法:
android:onclick=“clickHandler

在主程序类中
定义一个方法
public void clickHandler(view source)
{}

3.3基于回调的事件处理

对于基于回调的事件处理模型来说,事件源与事件监听器是统一的,或者说时间监听器完全消失了。当用户在GUI组件上激发某个事件时,组件自己特定的方法将会负责处理该事件。
为了使用回调机制类处理GUI组件上所发生的事件,我们需要为该组件提供对应的时间处理方法。,因为Java是静态语言只能继承GUI组件类。Android为所有GUI组件提供了一些时间处理的回调方法
boolean onkeyDown(int keyCode,KeyEvent event):用户在该组件上按下某个键时
boolean OnkeylongPress(int keyCode, KeyEvent event):当用户在组将上长按某个键。
boolean ontouchevent
boolean ontrackballevent:在组件上触发轨迹球屏幕时间时。
自定义一个按键类,重写了Button onkeydown的方法。
public boolean onKeyDown(int keyCode, KeyEvent event)
{
super.onKeyDown(keyCode, event);
Log.v("-crazyit.org-", "the onKeyDown in MyButton");
// 返回true,表明该事件不会向外扩散
return true;
}
<!-- 使用自定义View时应使用全限定类名 -->
<org.crazyit.event.MyButton  
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:text="单击我"
/>
基于回调的事件传播:
几乎所有基于回调的时间处理方法都有一个boolean类型的返回值,该返回值用于确定该处理方法能否完全处理该事件。
true表明已完全处理时间,事件不传播出去,false为并没完全处理事件,该事件会传播出去。
button的程序跟上面一样只是返回了false。

重写onTouchEvent方法响应触摸屏事件
不难发现监听的事件处理模型具有更大优势:
android保证基于监听的事件监听器会被优先触发。

3.4响应的西永设置的事件
configuration类专门用于描述手机设备上的配置信息,包括用户特定的配置也包括动态设备配置

EditText ori;
EditText navigation;
EditText touch;
EditText mnc;

// 获取应用界面中的界面组件
ori = (EditText)findViewById(R.id.ori);
navigation = (EditText)findViewById(R.id.navigation);
touch = (EditText)findViewById(R.id.touch);
mnc = (EditText)findViewById(R.id.mnc);

重写onConfigurationChanged响应系统设置更改
如果程序需要监听系统设置的更改,可以考虑重写Activity的onConfigurationChanged方法,当系统设置发生更改时,该方法被自动触发。
Configuration config = getResources().getConfiguration();
// 如果当前是横屏
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE)
{
// 设为竖屏
ChangeCfg.this.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
// 如果当前是竖屏
if (config.orientation == Configuration.ORIENTATION_PORTRAIT)
{
// 设为横屏
ChangeCfg.this.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}

// 重写该方法,用于监听系统设置的更改,主要是监控屏幕方向的更改
@Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
String screen = newConfig.orientation ==
Configuration.ORIENTATION_LANDSCAPE ? "横向屏幕" : "竖向屏幕";
Toast.makeText(this, "系统的屏幕方向发生改变" + "\n修改后的屏幕方向为:"
+ screen, Toast.LENGTH_LONG).show();
}

因此将应用的AndroidMainfest.xml文件改为如下形式:
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
<!-- 设置Activity可以监听屏幕方向改变的事件 -->
<activity android:configChanges="orientation"
android:name=".ChangeCfg"
android:label="@string/app_name">

Handeler消息传递机制:
android的UI操作并不是线程安全的,这意味着多线程安全问题,所以android指定一条规则:只允许UI线程修改Activity的UI组件。
当一个程序第一次启动时,Android会同时启动一条主线程,主线程会负责UI相关事件,按键,用户触屏,屏幕绘图等所以主线程也称为UI线程。
Handler类主要作用:
1.在新启动的线程中发送消息。
2.在主线程中获取、处理消息。
final ImageView show = (ImageView) findViewById(R.id.show);
final Handler myHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
// 如果该消息是本程序所发送的
if (msg.what == 0x1233)
{
// 动态地修改所显示的图片
show.setImageResource(imageIds[currentImageId++
% imageIds.length]);
}
}
};

new Timer().schedule(new TimerTask()
{
@Override
public void run()
{
// 发送空消息
myHandler.sendEmptyMessage(0x1233);
}
}, 0, 1200);

Handler、loop、MessageQueue工作原理

message:handler接受和处理的消息对象
looper:每个线程只能拥有一个looper。looper负责读取messagequeue,读取信息后把消息交给发送消息的Handler进行处理。
messagequeue:采用FIFO方式管理message程序创建Looper对象时会在它的构造器中创建looper对象
handler:如果希望handler正常工作,必须在当前线程有一个messagequeue,否则消息没地方保存,而messagequeue则是由looper处理所以必须要有一个looper对象。
两种处理方法:
1.主UI线程,系统已经初始化一个Looper对象直接创建handler即可。
2.自己创建的子线程,自己则必须创建乐观looper对象,并启动它。创建looper对象调用prepare方法即可。
定义一个子线程类。
class CalThread extends Thread
{
public Handler mHandler;


public void run()
{
Looper.prepare();
mHandler = new Handler()
{
// 定义处理消息的方法
@Override
public void handleMessage(Message msg)
{
if(msg.what == 0x123)
{
int upper = msg.getData().getInt(UPPER_NUM);
List<Integer> nums = new ArrayList<Integer>();
// 计算从2开始、到upper的所有质数
outer:
for (int i = 2 ; i <= upper ; i++)
{
// 用i处于从2开始、到i的平方根的所有数
for (int j = 2 ; j <= Math.sqrt(i) ; j++)
{
// 如果可以整除,表明这个数不是质数
if(i != 2 && i % j == 0)
{
continue outer;
}
}
nums.add(i);
}
// 使用Toast显示统计出来的所有质数
Toast.makeText(CalPrime.this , nums.toString()
, Toast.LENGTH_LONG).show();
}
}
};
Looper.loop();
}
}
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
etNum = (EditText)findViewById(R.id.etNum);
calThread = new CalThread();
// 启动新线程
calThread.start();
}
// 为按钮的点击事件提供事件处理函数
public void cal(View source)
{
// 创建消息
Message msg = new Message();
msg.what = 0x123;
Bundle bundle = new Bundle();
bundle.putInt(UPPER_NUM ,
Integer.parseInt(etNum.getText().toString()));
msg.setData(bundle);
// 向新线程中的Handler发送消息
calThread.mHandler.sendMessage(msg);
}

异步任务:AsyncTask
android默认UI线程阻塞超过20秒将会引发ANR异常。为了避免线程失去响应,建议耗时操作放在新线程中处理,为了解决新线程不能更新UI组件的问题,android提供了如下几种方案:
1.使用hanlder实现线程间的通讯。
2.activity.runOnUiThread(Runnable)
3.View.post(Runnable)
asyncTask是一个抽象类,通常用于被继承,继承时需要指定如下3个泛型参数。
相对来说asynctask更加轻量级一些,适用于简单的异步处理不需要借助线程和handler即可实现。
定义下三种参数:
Params:启动任务执行的输入参数类型。
progress:后台任务完成的进度值得类型;
result:后台执行任务后返回结果的类型。
使用异步类只要三步:
1.创建asynctask子类,并为上面3个参数指定类型。,如果某个参数不需要指定也可定义为void。
2.根据不同需要实现的方法如下
3.调用asynctask的之类的实例execute(params...params)开始执行耗时任务。
使用asynvtask时要遵守:
1.必须在UI线程中创建asynctask实例。
2.必须调用execute方法。


创建一个asycntask
// 重写该方法,为界面的按钮提供事件响应方法
public void download(View source) throws MalformedURLException
{
DownTask task = new DownTask(this);
task.execute(new URL("http://www.crazyit.org/ethos.php"));
}


0 0
原创粉丝点击