- 浏览: 929015 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
itzhongyuan:
java Random类详解 -
david_je:
你好,我看到你在C里面回调JAVA里面的方法是在native里 ...
Android NDK开发(1)----- Java与C互相调用实例详解 -
fykyx521:
请求锁是在 oncreate 释放实在ondestroy?? ...
Android如何保持程序一直运行 -
aduo_vip:
不错,总结得好!
Android读取assets目录下的资源 -
f839903061:
给的网址很给力哦!
Android 4.0.1 源码下载,编译和运行
1.android屏幕分辨率与load资源文件的问题:
举个例子:
在320X480的屏幕下,load一个300X150的图片
开始为了图省事,将图片资源都放在drawable-hdpi目录下,结果发现通过BitmapFactory.decodeResource函数load进来的Bitmap变成尺寸200X100了,缩小了1/3,然后就将图片放在drawable-mdpi目录下,load后的图片尺寸是正常的。
总结:看来应该是为了自适应屏幕,android将小屏幕的资源在大屏幕上load时,会缩小。
2.Android屏幕分辨率详解(VGA、HVGA、QVGA、WVGA、WQVGA)
这些术语都是指屏幕的分辨率。
VGA:Video Graphics Array,即:显示绘图矩阵,相当于640×480 像素;
HVGA:Half-size VGA;即:VGA的一半,分辨率为480×320;
QVGA:Quarter VGA;即:VGA的四分之一,分辨率为320×240;
WVGA:Wide Video Graphics Array;即:扩大的VGA,分辨率为800×480像素;
WQVGA:Wide Quarter VGA;即:扩大的QVGA,分辨率比QVGA高,比VGA低,一般是:400×240,480×272
Drawable(hdpi,ldpi,mdpi)的区别:
主要是为了支持多分辨率的.
hdpi里面主要放高分辨率的图片,如WVGA (480×800),FWVGA (480×854)
mdpi里面主要放中等分辨率的图片,如HVGA (320×480)
ldpi里面主要放低分辨率的图片,如QVGA (240×320)
系统会根据机器的分辨率来分别到这几个文件夹里面去找对应的图片
所以在开发程序时为了兼容不同平台不同屏幕,建议各自文件夹根据需求均存放不同版本图片。
总结:结合第1个注意事项自己总结吧!
3.Window
activity拥有一个或多个window (activity有一个Window成员,如果还是用popup对话框的话,则会拥有多个window)
一个activity的default window对应一个PhoneWindow, PhoneWindow中有过一个DecorView,它是所有view的根。
一个activity可以拥有一个或者多个window(如使用popup dialog)
另外,android支持surfaceView,这样一个window就可以拥有多个surface了
window->view hierachy(DecorView是tree的root)->ViewRoot->Surface
某一个view->surface
surfaceview是在view hierachy中embedded的surface
surfaceView类似于symbian中的DSA,直接访问surface
普通的是通过view访问surface
window manager会通过layer协调各个surface画图到frame buffer中去
一个activity可以拥有一个或者多个window(如使用popup dialog)
另外,android支持surfaceView,这样一个window就可以拥有多个surface了
window->view hierachy(DecorView是tree的root)->ViewRoot->Surface
某一个view->surface
surfaceview是在view hierachy中embedded的surface
surfaceView类似于symbian中的DSA,直接访问surface
普通的是通过view访问surface
window manager会通过layer协调各个surface画图到frame buffer中去
4:Canvas
android绘图最终绘制到surface上,它由window manager维护。
canvas相当于surface的设备环境,它提供了绘制的方法,并且指向surface
canvas是ViewRoot向window manager请求获取的,ViewRoot是客户端窗口(decor view及其child view)与Window Manager(surface)的桥梁,ViewRoot存在于window manager proxy中。
所有客户端view被动接受ViewRoot传递过来的canvas进行绘图
5.onInterceptTouchEvent和onTouchEvent调用时序
onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截,Android这么设计的想法也很好理解,由于ViewGroup会包含若干childView,因此需要能够统一监控各种touch事件的机会,因此纯粹的不能包含子view的控件是没有这个方法的,如LinearLayout就有,TextView就没有。
onInterceptTouchEvent()使用也很简单,如果在ViewGroup里覆写了该方法,那么就可以对各种touch事件加以拦截。但是如何拦截,是否所有的touch事件都需要拦截则是比较复杂的,touch事件在onInterceptTouchEvent()和onTouchEvent以及各个childView间的传递机制完全取决于onInterceptTouchEvent()和onTouchEvent()的返回值。并且,针对down事件处理的返回值直接影响到后续move和up事件的接收和传递。
关于返回值的问题,基本规则很清楚,如果return true,那么表示该方法消费了此次事件,如果return false,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理。
SDK给出的说明如下:
· You will receive the down event here.
· The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it). Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.
· For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target's onTouchEvent().
· If you return true from here, you will not receive any following events: the target view will receive the same event but with the action ACTION_CANCEL, and all further events will be delivered to your onTouchEvent() method and no longer appear here.
由于onInterceptTouchEvent()的机制比较复杂,上面的说明写的也比较复杂,总结一下,基本的规则是:
1.down事件首先会传递到onInterceptTouchEvent()方法
2.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。
3.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
4.如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
5.如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
5.onTouchEvent
onTouchEvent中要处理的最常用的3个事件就是:ACTION_DOWN、ACTION_MOVE、ACTION_UP。
这三个事件标识出了最基本的用户触摸屏幕的操作,含义也很清楚。虽然大家天天都在用它们,但是有一点请留意,ACTION_DOWN事件作为起始事件,它的重要性是要超过ACTION_MOVE和ACTION_UP的,如果发生了ACTION_MOVE或者ACTION_UP,那么一定曾经发生了ACTION_DOWN。
从Android的源代码中能看到基于这种不同重要性的理解而实现的一些交互机制,SDK中也有明确的提及,例如在ViewGroup的onInterceptTouchEvent方法中,如果在ACTION_DOWN事件中返回了true,那么后续的事件将直接发给onTouchEvent,而不是继续发给onInterceptTouchEvent。
6. onClick、onLongClick与onTouchEvent
曾经看过一篇帖子提到,如果在View中处理了onTouchEvent,那么就不用再处理onClick了,因为Android只会触发其中一个方法。这个理解是不太正确的,针对某个view,用户完成了一次触碰操作,显然从传感器上得到的信号是手指按下和抬起两个操作,我们可以理解为一次Click,也可以理解为发生了一次ACTION_DOWN和ACTION_UP,那么Android是如何理解和处理的呢?
在Android中,onClick、onLongClick的触发是和ACTION_DOWN及ACTION_UP相关的,在时序上,如果我们在一个View中同时覆写了onClick、onLongClick及onTouchEvent的话,onTouchEvent是最先捕捉到ACTION_DOWN和ACTION_UP事件的,其次才可能触发onClick或者onLongClick。主要的逻辑在View.java中的onTouchEvent方法中实现的:
case MotionEvent.ACTION_DOWN:
mPrivateFlags |= PRESSED;
refreshDrawableState();
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
postCheckForLongClick();
}
break;
case MotionEvent.ACTION_UP:
if ((mPrivateFlags & PRESSED) != 0) {
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (!mHasPerformedLongPress) {
if (mPendingCheckForLongPress != null) {
removeCallbacks(mPendingCheckForLongPress);
}
if (!focusTaken) {
performClick();
}
}
…
break;
可以看到,Click的触发是在系统捕捉到ACTION_UP后发生并由performClick()执行的,performClick里会调用先前注册的监听器的onClick()方法:
public boolean performClick() {
…
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
}
return false;
}
LongClick的触发则是从ACTION_DOWN开始,由postCheckForLongClick()方法完成:
private void postCheckForLongClick() {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout());
}
可以看到,在ACTION_DOWN事件被捕捉后,系统会开始触发一个postDelayed操作,delay的时间在Eclair2.1上为500ms,500ms后会触发CheckForLongPress线程的执行:
class CheckForLongPress implements Runnable {
…
public void run() {
if (isPressed() && (mParent != null)
&& mOriginalWindowAttachCount == mWindowAttachCount) {
if (performLongClick()) {
mHasPerformedLongPress = true;
}
}
}
…
}
如果各种条件都满足,那么在CheckForLongPress中执行performLongClick(),在这个方法中将调用onLongClick():
public boolean performLongClick() {
…
if (mOnLongClickListener != null) {
handled = mOnLongClickListener.onLongClick(View.this);
}
…
}
从实现中可以看到onClick()和onLongClick()方法是由ACTION_DOWN和ACTION_UP事件捕捉后根据各种情况最终确定是否触发的,也就是说如果我们在一个Activity或者View中同时监听或者覆写了onClick(),onLongClick()和onTouchEvent()方法,并不意味着只会发生其中一种。
下面是一个onClick被触发的基本时序的Log:
04-05 05:57:47.123: DEBUG/TSActivity(209): onTouch ACTION_DOWN
04-05 05:57:47.263: DEBUG/TSActivity(209): onTouch ACTION_UP
04-05 05:57:47.323: DEBUG/TSActivity(209): onClick
可以看出是按ACTION_DOWN -> ACTION_UP -> onClick的次序发生的。
下面是一个onLongClick被触发的基本时序的Log:
04-05 06:00:04.133: DEBUG/TSActivity(248): onTouch ACTION_DOWN
04-05 06:00:04.642: DEBUG/TSActivity(248): onLongClick
04-05 06:00:05.083: DEBUG/TSActivity(248): onTouch ACTION_UP
可以看到,在保持按下的状态一定时间后会触发onLongClick,之后抬起手才会发生ACTION_UP。
7.onClick和onLongClick能同时发生吗?
要弄清楚这个问题只要理解Android对事件处理的所谓消费(consume)概念即可,一个用户的操作会被传递到不同的View控件和同一个控件的不同监听方法处理,任何一个接收并处理了该次事件的方法如果在处理完后返回了true,那么该次event就算被完全处理了,其他的View或者监听方法就不会再有机会处理该event了。
onLongClick的发生是由单独的线程完成的,并且在ACTION_UP之前,而onClick的发生是在ACTION_UP后,因此同一次用户touch操作就有可能既发生onLongClick又发生onClick。这样是不是不可思议?所以及时向系统表示“我已经完全处理(消费)了用户的此次操作”,是很重要的事情。例如,我们如果在onLongClick()方法的最后return true,那么onClick事件就没有机会被触发了。
下面的Log是在onLongClick()方法return false的情况下,一次触碰操作的基本时序:
04-05 06:00:53.023: DEBUG/TSActivity(277): onTouch ACTION_DOWN
04-05 06:00:53.533: DEBUG/TSActivity(277): onLongClick
04-05 06:00:55.603: DEBUG/TSActivity(277): onTouch ACTION_UP
04-05 06:00:55.663: DEBUG/TSActivity(277): onClick
可以看到,在ACTION_UP后仍然触发了onClick()方法。
8.TextView加入链接的所有方法
1:使用android:autoLink="all" 只需在textview中加入这个属性 在里面写的文字中包含网址、电话、email的会自动加入连接地址。
如:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text1" android:layout_width="match_parent"
android:layout_height="match_parent" android:autoLink="all"
android:text="@string/link_text_auto" />
2:uses a string resource containing explicit <a> tags to specify
links.
如: <string name="link_text_manual"><b>text2:</b> This is some other
text, with a <a href="http://www.google.com">link</a> specified
via an <a> tag. Use a \"tel:\" URL
to <a href="tel:4155551212">dial a phone number</a>.
</string>
别忘了
TextView t2 = (TextView) findViewById(R.id.text2);
t2.setMovementMethod(LinkMovementMethod.getInstance());
3: builds the text in the Java code using HTML
TextView t3 = (TextView) findViewById(R.id.text3);
t3.setText(Html.fromHtml("<b>text3:</b> Text with a "
+ "<a href=\"http://www.google.com\">link</a> "
+ "created in the Java source code using HTML."));
t3.setMovementMethod(LinkMovementMethod.getInstance());
4:字符串截取方法
SpannableString ss = new SpannableString("text4: Click here to dial the phone.");
ss.setSpan(new StyleSpan(Typeface.BOLD), 0, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ss.setSpan(new URLSpan("tel:4155551212"), 13, 17, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView t4 = (TextView) findViewById(R.id.text4);
t4.setText(ss);
t4.setMovementMethod(LinkMovementMethod.getInstance());
Android中我们为了实现文本的滚动可以在ScrollView中嵌入一个TextView,其实TextView自己也可以实现多行滚动的,毕竟ScrollView必须只能有一个直接的子类布局。只要在layout中简单设置几个属性就可以轻松实现
<TextView
android:id="@+id/tvCWJ"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical" <!--垂直滚动条 -->
android:singleLine="false" <!--实现多行 -->
android:maxLines="15" <!--最多不超过15行 -->
android:textColor="#FF0000"
/>
当然我们为了让TextView动起来,还需要用到TextView的setMovementMethod方法设置一个滚动实例,代码如下
TextView tv = (TextView)findViewById(R.id.tvCWJ);
tv.setMovementMethod(ScrollingMovementMethod.getInstance()); // Android开发网提示相关的可以查看SDK中android.text.method分支了解更多
ad_link = (TextView) findViewById(R.id.ad_link);
ad_link.setText(Html.fromHtml("<a href="\" mce_href="\"""+mURL.getLink()+"\">"+Html.fromHtml(mURL.getLabel()+"</a>")));
ad_link.setMovementMethod(LinkMovementMethod.getInstance());
9.DecorView 和 ViewRoot 的关系
ActivityThread.java中调用wm.addView(decor, l);把它加入到window manager proxy的mViews中,同时为这个decor view创建一个ViewRoot,ViewRoot负责协调decor view与window manager直接绘图、事件处理。
ViewRoot中有IWindowSession和IWindow用来和window manger打交道和接收window manager传过来的消息,消息传过来后ViewRoot分发给decor view,再由decor view进行分发,window manager proxy中维护了view, ViewRoot, layout param三元组。每次调用window manager proxy的addView都会新增一个三元组。一般程序中都是调用addView(decor,...),也就是只对decor view调用addView 。
10.android Home键 屏蔽,捕获,修改
开发过程中相信大家都有碰到因为不能捕获Home键而烦恼,现在终于有办法了,在Level5以上(包含)中,Activity类中有如下方法:
public void onAttachedToWindow ()
Since: API Level 5
Called when the main window associated with the activity has been attached to the window manager. See View.onAttachedToWindow() for more information.
See Also
* onAttachedToWindow()
private boolean catchHomeKey = false;
@Override
public void onAttachedToWindow() {
// TODO Auto-generated method stub
if(catchHomeKey) {
this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);
}
super.onAttachedToWindow();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if(keyCode == KeyEvent.KEYCODE_HOME) {
Log.e(TAG, "Home key down");
}
return super.onKeyDown(keyCode, event);
}
重写Activity中的onAttachedToWindow方法,设置Type,就能捕获到Home键。
当不需要捕获时,删除setType这一行就OK。
11.获取Android的SdkVersion版本号
int version = Integer.valueOf(android.os.Build.VERSION.SDK);
12.获取手机本地参数
入口:TelephonyManager tm = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
13.获取信号强度信息
public class GetGsmSignalStrength extends Activity
{
/* This variables need to be global, so we can used them onResume and onPause method to
stop the listener */
TelephonyManager Tel;
MyPhoneStateListener MyListener;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/* Update the listener, and start it */
MyListener = new MyPhoneStateListener();
Tel = ( TelephonyManager )getSystemService(Context.TELEPHONY_SERVICE);
Tel.listen(MyListener ,PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
/* Called when the application is minimized */
@Override
protected void onPause()
{
super.onPause();
Tel.listen(MyListener, PhoneStateListener.LISTEN_NONE);
}
/* Called when the application resumes */
@Override
protected void onResume()
{
super.onResume();
Tel.listen(MyListener,PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
/* —————————– */
/* Start the PhoneState listener */
/* —————————– */
private class MyPhoneStateListener extends PhoneStateListener
{
/* Get the Signal strength from the provider, each tiome there is an update */
@Override
public void onSignalStrengthsChanged(SignalStrength signalStrength)
{
super.onSignalStrengthsChanged(signalStrength);
Toast.makeText(getApplicationContext(), "Go to Firstdroid!!! GSM Cinr = "
+ String.valueOf(signalStrength.getGsmSignalStrength()), Toast.LENGTH_SHORT).show();
}
};/* End of private Class */
}/* GetGsmSignalStrength */
需要添加权限:
<uses -permission android:name="android.permission.CHANGE_NETWORK_STATE"></uses>
14.自定义View设置Shadow阴影
TextView可以利用shadowColor来设置文字阴影,这里分享另一种设置阴影的方法
可以利用Paint的setShadowLayer实现自定义View的shadow。
package com.devdiv.myshadow2;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.os.Bundle;
import android.view.View;
public class MyShadow2 extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView( new drawCanvas(this) );
}
class drawCanvas extends View {
public drawCanvas(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 建立Paint对象
Paint vPaint = new Paint();
Paint vPaint2 = new Paint();
// --------------------------------------------
// 设定颜色
vPaint.setColor(0xFFFFFF00);
// 实心矩形,看左上矩形
canvas.drawRect( 30
, 50
, 130
, 150
, vPaint
);
// 设定阴影(柔边, X 轴位移, Y 轴位移, 阴影颜色)
vPaint .setShadowLayer (5, 3, 3, 0xFFFF00FF);
// 实心矩形& 其阴影,看左下矩形
canvas.drawRect( 30
, 200
, 130
, 300
, vPaint
);
// --------------------------------------------
// 设定颜色
vPaint2.setColor(0xFFFFFF00);
// 空心
vPaint2 .setStyle(Style.STROKE);
// 空心矩形,看右上矩形
canvas.drawRect( 200
, 50
, 300
, 150
, vPaint2
);
// 设定阴影(柔边, X 轴位移, Y 轴位移, 阴影颜色)
vPaint2 .setShadowLayer (5, 3, 3, 0xFFFF00FF);
// 空心矩形& 其阴影,看右下矩形
canvas.drawRect( 200
, 200
, 300
, 300
, vPaint2
);
}
}
}
举个例子:
在320X480的屏幕下,load一个300X150的图片
开始为了图省事,将图片资源都放在drawable-hdpi目录下,结果发现通过BitmapFactory.decodeResource函数load进来的Bitmap变成尺寸200X100了,缩小了1/3,然后就将图片放在drawable-mdpi目录下,load后的图片尺寸是正常的。
总结:看来应该是为了自适应屏幕,android将小屏幕的资源在大屏幕上load时,会缩小。
2.Android屏幕分辨率详解(VGA、HVGA、QVGA、WVGA、WQVGA)
这些术语都是指屏幕的分辨率。
VGA:Video Graphics Array,即:显示绘图矩阵,相当于640×480 像素;
HVGA:Half-size VGA;即:VGA的一半,分辨率为480×320;
QVGA:Quarter VGA;即:VGA的四分之一,分辨率为320×240;
WVGA:Wide Video Graphics Array;即:扩大的VGA,分辨率为800×480像素;
WQVGA:Wide Quarter VGA;即:扩大的QVGA,分辨率比QVGA高,比VGA低,一般是:400×240,480×272
Drawable(hdpi,ldpi,mdpi)的区别:
主要是为了支持多分辨率的.
hdpi里面主要放高分辨率的图片,如WVGA (480×800),FWVGA (480×854)
mdpi里面主要放中等分辨率的图片,如HVGA (320×480)
ldpi里面主要放低分辨率的图片,如QVGA (240×320)
系统会根据机器的分辨率来分别到这几个文件夹里面去找对应的图片
所以在开发程序时为了兼容不同平台不同屏幕,建议各自文件夹根据需求均存放不同版本图片。
总结:结合第1个注意事项自己总结吧!
3.Window
activity拥有一个或多个window (activity有一个Window成员,如果还是用popup对话框的话,则会拥有多个window)
一个activity的default window对应一个PhoneWindow, PhoneWindow中有过一个DecorView,它是所有view的根。
一个activity可以拥有一个或者多个window(如使用popup dialog)
另外,android支持surfaceView,这样一个window就可以拥有多个surface了
window->view hierachy(DecorView是tree的root)->ViewRoot->Surface
某一个view->surface
surfaceview是在view hierachy中embedded的surface
surfaceView类似于symbian中的DSA,直接访问surface
普通的是通过view访问surface
window manager会通过layer协调各个surface画图到frame buffer中去
一个activity可以拥有一个或者多个window(如使用popup dialog)
另外,android支持surfaceView,这样一个window就可以拥有多个surface了
window->view hierachy(DecorView是tree的root)->ViewRoot->Surface
某一个view->surface
surfaceview是在view hierachy中embedded的surface
surfaceView类似于symbian中的DSA,直接访问surface
普通的是通过view访问surface
window manager会通过layer协调各个surface画图到frame buffer中去
4:Canvas
android绘图最终绘制到surface上,它由window manager维护。
canvas相当于surface的设备环境,它提供了绘制的方法,并且指向surface
canvas是ViewRoot向window manager请求获取的,ViewRoot是客户端窗口(decor view及其child view)与Window Manager(surface)的桥梁,ViewRoot存在于window manager proxy中。
所有客户端view被动接受ViewRoot传递过来的canvas进行绘图
5.onInterceptTouchEvent和onTouchEvent调用时序
onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截,Android这么设计的想法也很好理解,由于ViewGroup会包含若干childView,因此需要能够统一监控各种touch事件的机会,因此纯粹的不能包含子view的控件是没有这个方法的,如LinearLayout就有,TextView就没有。
onInterceptTouchEvent()使用也很简单,如果在ViewGroup里覆写了该方法,那么就可以对各种touch事件加以拦截。但是如何拦截,是否所有的touch事件都需要拦截则是比较复杂的,touch事件在onInterceptTouchEvent()和onTouchEvent以及各个childView间的传递机制完全取决于onInterceptTouchEvent()和onTouchEvent()的返回值。并且,针对down事件处理的返回值直接影响到后续move和up事件的接收和传递。
关于返回值的问题,基本规则很清楚,如果return true,那么表示该方法消费了此次事件,如果return false,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理。
SDK给出的说明如下:
· You will receive the down event here.
· The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it). Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.
· For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target's onTouchEvent().
· If you return true from here, you will not receive any following events: the target view will receive the same event but with the action ACTION_CANCEL, and all further events will be delivered to your onTouchEvent() method and no longer appear here.
由于onInterceptTouchEvent()的机制比较复杂,上面的说明写的也比较复杂,总结一下,基本的规则是:
1.down事件首先会传递到onInterceptTouchEvent()方法
2.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。
3.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
4.如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
5.如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
5.onTouchEvent
onTouchEvent中要处理的最常用的3个事件就是:ACTION_DOWN、ACTION_MOVE、ACTION_UP。
这三个事件标识出了最基本的用户触摸屏幕的操作,含义也很清楚。虽然大家天天都在用它们,但是有一点请留意,ACTION_DOWN事件作为起始事件,它的重要性是要超过ACTION_MOVE和ACTION_UP的,如果发生了ACTION_MOVE或者ACTION_UP,那么一定曾经发生了ACTION_DOWN。
从Android的源代码中能看到基于这种不同重要性的理解而实现的一些交互机制,SDK中也有明确的提及,例如在ViewGroup的onInterceptTouchEvent方法中,如果在ACTION_DOWN事件中返回了true,那么后续的事件将直接发给onTouchEvent,而不是继续发给onInterceptTouchEvent。
6. onClick、onLongClick与onTouchEvent
曾经看过一篇帖子提到,如果在View中处理了onTouchEvent,那么就不用再处理onClick了,因为Android只会触发其中一个方法。这个理解是不太正确的,针对某个view,用户完成了一次触碰操作,显然从传感器上得到的信号是手指按下和抬起两个操作,我们可以理解为一次Click,也可以理解为发生了一次ACTION_DOWN和ACTION_UP,那么Android是如何理解和处理的呢?
在Android中,onClick、onLongClick的触发是和ACTION_DOWN及ACTION_UP相关的,在时序上,如果我们在一个View中同时覆写了onClick、onLongClick及onTouchEvent的话,onTouchEvent是最先捕捉到ACTION_DOWN和ACTION_UP事件的,其次才可能触发onClick或者onLongClick。主要的逻辑在View.java中的onTouchEvent方法中实现的:
case MotionEvent.ACTION_DOWN:
mPrivateFlags |= PRESSED;
refreshDrawableState();
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
postCheckForLongClick();
}
break;
case MotionEvent.ACTION_UP:
if ((mPrivateFlags & PRESSED) != 0) {
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (!mHasPerformedLongPress) {
if (mPendingCheckForLongPress != null) {
removeCallbacks(mPendingCheckForLongPress);
}
if (!focusTaken) {
performClick();
}
}
…
break;
可以看到,Click的触发是在系统捕捉到ACTION_UP后发生并由performClick()执行的,performClick里会调用先前注册的监听器的onClick()方法:
public boolean performClick() {
…
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
}
return false;
}
LongClick的触发则是从ACTION_DOWN开始,由postCheckForLongClick()方法完成:
private void postCheckForLongClick() {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout());
}
可以看到,在ACTION_DOWN事件被捕捉后,系统会开始触发一个postDelayed操作,delay的时间在Eclair2.1上为500ms,500ms后会触发CheckForLongPress线程的执行:
class CheckForLongPress implements Runnable {
…
public void run() {
if (isPressed() && (mParent != null)
&& mOriginalWindowAttachCount == mWindowAttachCount) {
if (performLongClick()) {
mHasPerformedLongPress = true;
}
}
}
…
}
如果各种条件都满足,那么在CheckForLongPress中执行performLongClick(),在这个方法中将调用onLongClick():
public boolean performLongClick() {
…
if (mOnLongClickListener != null) {
handled = mOnLongClickListener.onLongClick(View.this);
}
…
}
从实现中可以看到onClick()和onLongClick()方法是由ACTION_DOWN和ACTION_UP事件捕捉后根据各种情况最终确定是否触发的,也就是说如果我们在一个Activity或者View中同时监听或者覆写了onClick(),onLongClick()和onTouchEvent()方法,并不意味着只会发生其中一种。
下面是一个onClick被触发的基本时序的Log:
04-05 05:57:47.123: DEBUG/TSActivity(209): onTouch ACTION_DOWN
04-05 05:57:47.263: DEBUG/TSActivity(209): onTouch ACTION_UP
04-05 05:57:47.323: DEBUG/TSActivity(209): onClick
可以看出是按ACTION_DOWN -> ACTION_UP -> onClick的次序发生的。
下面是一个onLongClick被触发的基本时序的Log:
04-05 06:00:04.133: DEBUG/TSActivity(248): onTouch ACTION_DOWN
04-05 06:00:04.642: DEBUG/TSActivity(248): onLongClick
04-05 06:00:05.083: DEBUG/TSActivity(248): onTouch ACTION_UP
可以看到,在保持按下的状态一定时间后会触发onLongClick,之后抬起手才会发生ACTION_UP。
7.onClick和onLongClick能同时发生吗?
要弄清楚这个问题只要理解Android对事件处理的所谓消费(consume)概念即可,一个用户的操作会被传递到不同的View控件和同一个控件的不同监听方法处理,任何一个接收并处理了该次事件的方法如果在处理完后返回了true,那么该次event就算被完全处理了,其他的View或者监听方法就不会再有机会处理该event了。
onLongClick的发生是由单独的线程完成的,并且在ACTION_UP之前,而onClick的发生是在ACTION_UP后,因此同一次用户touch操作就有可能既发生onLongClick又发生onClick。这样是不是不可思议?所以及时向系统表示“我已经完全处理(消费)了用户的此次操作”,是很重要的事情。例如,我们如果在onLongClick()方法的最后return true,那么onClick事件就没有机会被触发了。
下面的Log是在onLongClick()方法return false的情况下,一次触碰操作的基本时序:
04-05 06:00:53.023: DEBUG/TSActivity(277): onTouch ACTION_DOWN
04-05 06:00:53.533: DEBUG/TSActivity(277): onLongClick
04-05 06:00:55.603: DEBUG/TSActivity(277): onTouch ACTION_UP
04-05 06:00:55.663: DEBUG/TSActivity(277): onClick
可以看到,在ACTION_UP后仍然触发了onClick()方法。
8.TextView加入链接的所有方法
1:使用android:autoLink="all" 只需在textview中加入这个属性 在里面写的文字中包含网址、电话、email的会自动加入连接地址。
如:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text1" android:layout_width="match_parent"
android:layout_height="match_parent" android:autoLink="all"
android:text="@string/link_text_auto" />
2:uses a string resource containing explicit <a> tags to specify
links.
如: <string name="link_text_manual"><b>text2:</b> This is some other
text, with a <a href="http://www.google.com">link</a> specified
via an <a> tag. Use a \"tel:\" URL
to <a href="tel:4155551212">dial a phone number</a>.
</string>
别忘了
TextView t2 = (TextView) findViewById(R.id.text2);
t2.setMovementMethod(LinkMovementMethod.getInstance());
3: builds the text in the Java code using HTML
TextView t3 = (TextView) findViewById(R.id.text3);
t3.setText(Html.fromHtml("<b>text3:</b> Text with a "
+ "<a href=\"http://www.google.com\">link</a> "
+ "created in the Java source code using HTML."));
t3.setMovementMethod(LinkMovementMethod.getInstance());
4:字符串截取方法
SpannableString ss = new SpannableString("text4: Click here to dial the phone.");
ss.setSpan(new StyleSpan(Typeface.BOLD), 0, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ss.setSpan(new URLSpan("tel:4155551212"), 13, 17, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView t4 = (TextView) findViewById(R.id.text4);
t4.setText(ss);
t4.setMovementMethod(LinkMovementMethod.getInstance());
Android中我们为了实现文本的滚动可以在ScrollView中嵌入一个TextView,其实TextView自己也可以实现多行滚动的,毕竟ScrollView必须只能有一个直接的子类布局。只要在layout中简单设置几个属性就可以轻松实现
<TextView
android:id="@+id/tvCWJ"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical" <!--垂直滚动条 -->
android:singleLine="false" <!--实现多行 -->
android:maxLines="15" <!--最多不超过15行 -->
android:textColor="#FF0000"
/>
当然我们为了让TextView动起来,还需要用到TextView的setMovementMethod方法设置一个滚动实例,代码如下
TextView tv = (TextView)findViewById(R.id.tvCWJ);
tv.setMovementMethod(ScrollingMovementMethod.getInstance()); // Android开发网提示相关的可以查看SDK中android.text.method分支了解更多
ad_link = (TextView) findViewById(R.id.ad_link);
ad_link.setText(Html.fromHtml("<a href="\" mce_href="\"""+mURL.getLink()+"\">"+Html.fromHtml(mURL.getLabel()+"</a>")));
ad_link.setMovementMethod(LinkMovementMethod.getInstance());
9.DecorView 和 ViewRoot 的关系
ActivityThread.java中调用wm.addView(decor, l);把它加入到window manager proxy的mViews中,同时为这个decor view创建一个ViewRoot,ViewRoot负责协调decor view与window manager直接绘图、事件处理。
ViewRoot中有IWindowSession和IWindow用来和window manger打交道和接收window manager传过来的消息,消息传过来后ViewRoot分发给decor view,再由decor view进行分发,window manager proxy中维护了view, ViewRoot, layout param三元组。每次调用window manager proxy的addView都会新增一个三元组。一般程序中都是调用addView(decor,...),也就是只对decor view调用addView 。
10.android Home键 屏蔽,捕获,修改
开发过程中相信大家都有碰到因为不能捕获Home键而烦恼,现在终于有办法了,在Level5以上(包含)中,Activity类中有如下方法:
public void onAttachedToWindow ()
Since: API Level 5
Called when the main window associated with the activity has been attached to the window manager. See View.onAttachedToWindow() for more information.
See Also
* onAttachedToWindow()
private boolean catchHomeKey = false;
@Override
public void onAttachedToWindow() {
// TODO Auto-generated method stub
if(catchHomeKey) {
this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);
}
super.onAttachedToWindow();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if(keyCode == KeyEvent.KEYCODE_HOME) {
Log.e(TAG, "Home key down");
}
return super.onKeyDown(keyCode, event);
}
重写Activity中的onAttachedToWindow方法,设置Type,就能捕获到Home键。
当不需要捕获时,删除setType这一行就OK。
11.获取Android的SdkVersion版本号
int version = Integer.valueOf(android.os.Build.VERSION.SDK);
12.获取手机本地参数
入口:TelephonyManager tm = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
13.获取信号强度信息
public class GetGsmSignalStrength extends Activity
{
/* This variables need to be global, so we can used them onResume and onPause method to
stop the listener */
TelephonyManager Tel;
MyPhoneStateListener MyListener;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/* Update the listener, and start it */
MyListener = new MyPhoneStateListener();
Tel = ( TelephonyManager )getSystemService(Context.TELEPHONY_SERVICE);
Tel.listen(MyListener ,PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
/* Called when the application is minimized */
@Override
protected void onPause()
{
super.onPause();
Tel.listen(MyListener, PhoneStateListener.LISTEN_NONE);
}
/* Called when the application resumes */
@Override
protected void onResume()
{
super.onResume();
Tel.listen(MyListener,PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
/* —————————– */
/* Start the PhoneState listener */
/* —————————– */
private class MyPhoneStateListener extends PhoneStateListener
{
/* Get the Signal strength from the provider, each tiome there is an update */
@Override
public void onSignalStrengthsChanged(SignalStrength signalStrength)
{
super.onSignalStrengthsChanged(signalStrength);
Toast.makeText(getApplicationContext(), "Go to Firstdroid!!! GSM Cinr = "
+ String.valueOf(signalStrength.getGsmSignalStrength()), Toast.LENGTH_SHORT).show();
}
};/* End of private Class */
}/* GetGsmSignalStrength */
需要添加权限:
<uses -permission android:name="android.permission.CHANGE_NETWORK_STATE"></uses>
14.自定义View设置Shadow阴影
TextView可以利用shadowColor来设置文字阴影,这里分享另一种设置阴影的方法
可以利用Paint的setShadowLayer实现自定义View的shadow。
package com.devdiv.myshadow2;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.os.Bundle;
import android.view.View;
public class MyShadow2 extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView( new drawCanvas(this) );
}
class drawCanvas extends View {
public drawCanvas(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 建立Paint对象
Paint vPaint = new Paint();
Paint vPaint2 = new Paint();
// --------------------------------------------
// 设定颜色
vPaint.setColor(0xFFFFFF00);
// 实心矩形,看左上矩形
canvas.drawRect( 30
, 50
, 130
, 150
, vPaint
);
// 设定阴影(柔边, X 轴位移, Y 轴位移, 阴影颜色)
vPaint .setShadowLayer (5, 3, 3, 0xFFFF00FF);
// 实心矩形& 其阴影,看左下矩形
canvas.drawRect( 30
, 200
, 130
, 300
, vPaint
);
// --------------------------------------------
// 设定颜色
vPaint2.setColor(0xFFFFFF00);
// 空心
vPaint2 .setStyle(Style.STROKE);
// 空心矩形,看右上矩形
canvas.drawRect( 200
, 50
, 300
, 150
, vPaint2
);
// 设定阴影(柔边, X 轴位移, Y 轴位移, 阴影颜色)
vPaint2 .setShadowLayer (5, 3, 3, 0xFFFF00FF);
// 空心矩形& 其阴影,看右下矩形
canvas.drawRect( 200
, 200
, 300
, 300
, vPaint2
);
}
}
}
发表评论
-
Android使用binder访问service的方式
2013-08-23 09:42 16311. 我们先来看一个与本地service通信的例子。 pub ... -
android-Service和Thread的区别
2013-08-23 09:17 914servie是系统的组件,它由系统进程托管(servicema ... -
git介绍
2013-08-01 14:49 1035git介绍 使用Git的第一件事就是设置你的名字和email ... -
cocos2d-x学习之自动内存管理和常见宏
2013-07-29 15:41 9081.自动内存管理 1)概述 C++语言默认是 ... -
cocos2dx中利用xcode 调用java中的函数
2013-07-29 11:36 25251. 先把cocos2dx根目录中的 /Users/zhaos ... -
cocos2dx(v2.x)与(v1.x)的一些常用函数区别讲解
2013-07-29 10:35 1109第一个改动: CCLayer初始化 自定义Layer,类名 ... -
xcode与eclipse整合cocos2dx
2013-07-29 10:32 1220文档xcode版本是 204 1. 在xcode中创建coc ... -
git提交代码
2013-07-23 16:00 10531. 在本地创建一个Git的工作空间,在里面创建一个工程(如H ... -
Android.mk的用法和基础
2013-07-19 14:11 4332一个Android.mk file用来向编译系统描述你的源代码 ... -
eclipse配置NDK-Builder命令
2013-07-18 11:02 10351. 2. -
eclipse配置javah命令
2013-07-18 10:48 19931.找到javah命令所在的目录 我的为 /usr/bi ... -
Android SDL2.0 编译
2013-07-17 13:40 19671,下载: wget http://www.libsdl.o ... -
IntelliJ Idea 常用快捷键列表
2013-05-27 10:19 0Alt+回车 导入包,自动修 ... -
android应用后台安装
2013-05-21 12:02 1015android应用后台安装,静默安装的代码实现方法 http ... -
编译linux内核映像
2013-05-21 11:33 962a)准备交叉编译工具链 android代码树中有一个pr ... -
如何单独编译Android源代码中的模块
2013-05-21 11:29 994一. 首先在Android源代码 ... -
Ubuntu安装JDK6和JDK5
2013-05-19 19:04 1006sudo apt-get install sun-java6- ... -
java_jni详解_01
2013-05-08 17:15 956java中的jni 例子HelloWorld 准备过程: 1 ... -
下载android源码 中断解决原因
2013-05-07 15:51 1315解决方法 1. 浏览器登录https://android.go ... -
mac下编译ffmpeg1.1.4
2013-05-07 14:55 1364经过一番网上搜索 与 无数次的编译 终于成功了 下面献上编译 ...
相关推荐
第1章 Android开发简介 1.1 Android基本概念 1.1.1 Android简介 1.1.2 Android的系统构架 1.1.3 Android应用程序框架 1.2 OMS介绍 1.2.1 OPhone介绍 1.2.2 Widget介绍 1.3 小结 第2章 Android开发环境搭建 2.1 ...
总之,"Android开发入门60个小案例+源代码"是一个宝贵的资源,它涵盖了Android开发的众多基础知识点,通过实践这些小案例,初学者可以快速上手,逐步掌握Android应用开发的核心技术。同时,源代码提供了直接参考和...
第2章 工欲善其事 必先利其器——搭建Android开发环境 2.1 开发Android应用前的准备 2.1.1 Android开发系统要求 2.1.2 Android软件开发包 2.1.3 其他注意事项 2.2 Windows开发环境搭建 2.2.1 JDK、Eclipse、Android...
本资源包“android开发图标素材”包含了一系列与天气相关的图标以及各种应用程序常用的图标,旨在方便开发者们在创建Android应用时使用。 首先,我们要了解Android图标设计的基本原则。在Android平台上,图标应当...
- **Eclipse with ADT Plugin**:早期Android开发的主要工具,虽然已被Android Studio取代,但在某些特定情况下仍可能被使用。 ##### 2. 安装Java Development Kit (JDK) - Android开发基于Java语言,因此需要安装...
4. **耗电优化**:注意网络请求和后台服务的使用,减少不必要的CPU使用率。 5. **代码质量和可维护性**:遵循良好的编码规范,使用MVC或MVVM等架构模式提高代码的可读性和可扩展性。 ### Android应用发布流程 1. *...
9. **Android开发编程从入门到精通——Android程序员必备.doc**:这是一本完整的Android编程教程,从基础概念到高级技巧,全面覆盖了Android开发的各个方面,适合想要系统学习Android开发的读者。 10. **Android...
《Android开发帮助文档中文版》是一份非常宝贵的资源,它为开发者提供了全面的Android平台开发指导,涵盖了从基础概念到高级特性的全方位讲解。这份文档是2016年12月21日的最新版本,确保了内容的时效性和准确性,...
知识点一:Android桌面启动器(Launcher)开发基础 在Android系统中,桌面启动器(Launcher)是用户与Android设备交互的起点,负责展示应用图标、列表和各种小部件。Android允许开发者创建自己的Launcher应用,可以...
由于无法直接访问提供的文件内容,以上知识点为基于标题和描述所推测的Android开发可能涵盖的主题。在实际学习《Android开发精要.pdf》时,读者应当注意对每个知识点进行深入学习,并通过实践操作来加强理解。如果...
### Android开发环境搭建所需软件及安装指南 #### 一、概览 随着移动互联网的快速发展,Android操作系统作为全球市场份额最大的移动操作系统,其应用开发需求日益增长。为了进行Android应用程序的开发,开发者首先...
Android SDK包含了开发Android应用所需的工具和库文件,是进行Android开发的基础。 - **下载地址**:由于版权和网络原因,官方提供的下载链接可能无法直接访问,需要自行寻找其他途径下载SDK。 - **注意事项**: -...
Android串口开发需要注意设备兼容性问题,因为并非所有Android设备都支持串口通信。某些设备可能需要依赖USB转串口适配器,这时就需要利用Android的USB Host API。 通过理解以上知识点,开发者可以构建一个功能...
【Android开发视频教学源码详解】 在Android开发领域,源码是学习和理解技术的关键,因为它们揭示了实际应用的工作原理。"Android开发视频教学源码"是一个宝贵的资源,为初学者和经验丰富的开发者提供了实践操作的...
1. **环境配置**:确保Android开发环境(如Android Studio)已安装,并且设备或模拟器支持所需的硬件接口。 2. **导入SDK**:将SDK提供的库文件导入到Android项目中,通常可以通过Gradle依赖或者手动添加JAR/AAR文件...
### Android开发环境搭建知识点 #### 一、软件准备 **1. JDK (Java Development Kit)** - **定义**: JDK是Java开发工具包,包含了Java虚拟机(JVM)、Java核心类库和支持文件等,是Android开发的基础。 - **下载...
在进入Android开发的世界之前,首先需要搭建一个完整的开发环境。这个环境包括了各种必要的软件工具,使得开发者能够编写、编译、调试以及运行Android应用程序。本文将详细介绍如何一步步搭建史上最全的Android开发...
《名师讲坛:Android开发实战经典》是一本深入探讨Android应用程序开发的专业书籍,以其PDF格式提供了丰富的知识内容。这本书旨在帮助读者从理论到实践全面掌握Android开发技能,无论是初学者还是有一定经验的开发者...