一、Surface
Surface就是“表面”的意思。在SDK的文档中,对Surface的描述是这样的:“Handle onto a raw buffer that is being managed by the screen compositor”,翻译成中文就是“由屏幕显示内容合成器(screen compositor)所管理的原生缓冲器的句柄”,这句话包括下面两个意思:
1. 通过Surface(因为Surface是句柄)就可以获得原生缓冲器以及其中的内容。就像在C语言中,可以通过一个文件的句柄,就可以获得文件的内容一样;
2. 原生缓冲器(rawbuffer)是用于保存当前窗口的像素数据的。
引伸地,可以认为Android中的Surface就是一个用来画图形(graphics)或图像(image)的地方。根据Java方面的常规知识,我们知道通常画图是在一个Canvas对象上面进行的,由此,可以推知一个Surface对象中应该包含有一个Canvas对象,事实上的确如此,而且这一点可以很容易通过debug运行程序的方式得到证明(将光标停留在对象变量surface上,会弹出一个对话框,其中红色方框的内容,就表面surface中有一个CompatileCanvas成员变量)当然,看源代码也是可以证明这一点:
因此,在前面提及的两个意思的基础上,可以再加上一条:
3. Surface中有一个Canvas成员,专门用于画图的。
所以,Surface中的Canvas成员,是专门用于供程序员画图的场所,就像黑板一样;其中的原生缓冲器是用来保存数据的地方;Surface本身的作用类似一个句柄,得到了这个句柄就可以得到其中的Canvas、原生缓冲器以及其它方面的内容。
二、SurfaceView
SurfaceView,顾名思义就是Surface的View,通过SurfaceView就可以看到Surface的部分或者全部的内容,下面用一个图来形象地描述一下Surface和SurfaceView的关系:
也就是说,Surface是用通过SurfaceView才能展示其中的内容。从这个意思上来说,SurfaceView中的View之确切的含义应该是viewport即“视口”的意思,做过数据库设计的朋友知道,假定一个数据表有20个字段,但我们常常只用到其中的5个字段,那么就可以在原数据表的基础上,通过SQL语句CREATEVIEW来创建只包含那5个字段内容的view。
另一方面,SurfaceView是Android中View的子类。事实上,在Android中所有用于界面展示的类皆为View的子类,包括那些不可见的、各种各样的Layout。
所以说,SurfaceView中的View有两个含义:
1. 视口(viewport)的意思
2. SurfaceView是View的派生类
在Android中Surface是从Object派生而来,且实现了Parcelable接口。看到Parcelable就让人能很自然地想到数据容器,SurfaceView就是用来展示Surface中的数据的。在这个层面上而言,Surface就是管理数据的地方,SurfaceView就是展示数据的地方。
三、SurfaceHolder
SurfaceHolder是一个接口,其作用就像一个关于Surface的监听器。提供访问和控制SurfaceView背后的Surface 相关的方法 (providingaccess and control over this SurfaceView's underlying surface),它通过三个回调方法,让我们可以感知到Surface的创建、销毁或者改变。在SurfaceView中有一个方法getHolder,可以很方便地获得SurfaceView所对应的Surface所对应的SurfaceHolder(有点拗口吧)。
除下面将要提到的SurfaceHolder.Callback外,SurfaceHolder还提供了很多重要的方法,其中最重要的就是:
1. abstract void addCallback(SurfaceHolder.Callbackcallback)
为SurfaceHolder添加一个SurfaceHolder.Callback回调接口。
2. abstract CanvaslockCanvas()
获取一个Canvas对象,并锁定之。所得到的Canvas对象,其实就是Surface中一个成员。
3. abstract CanvaslockCanvas(Rectdirty)
同上。但只锁定dirty所指定的矩形区域,因此效率更高。
4. abstract void unlockCanvasAndPost(Canvascanvas)
当修改Surface中的数据完成后,释放同步锁,并提交改变,然后将新的数据进行展示,同时Surface中相关数据会被丢失。
5. public abstract void setType (int type)
设置Surface的类型,接收如下的参数:
SURFACE_TYPE_NORMAL:用RAM缓存原生数据的普通Surface
SURFACE_TYPE_HARDWARE:适用于DMA(Direct memory access )引擎和硬件加速的Surface
SURFACE_TYPE_GPU:适用于GPU加速的Surface
SURFACE_TYPE_PUSH_BUFFERS:表明该Surface不包含原生数据,Surface用到的数据由其他对象提供,在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数据,这样图像预览会比较流畅。如果设置这种类型则就不能调用lockCanvas来获取Canvas对象了。需要注意的是,在高版本的Android SDK中,setType这个方法已经被depreciated了。
2、3、4中的同步锁机制的目的,就是为了在绘制的过程中,Surface中的数据不会被改变。
从设计模式的高度来看,Surface、SurfaceView和SurfaceHolder实质上就是广为人知的MVC,即Model-View-Controller。Model就是模型的意思,或者说是数据模型,或者更简单地说就是数据,也就是这里的Surface;View即视图,代表用户交互界面,也就是这里的SurfaceView;SurfaceHolder很明显可以理解为MVC中的Controller(控制器)。这样看起来三者之间的关系就清楚了很多。
四、SurfaceHolder.Callback
前面已经讲到SurfaceHolder是一个接口,它通过回到方法的方式,让我们可以感知到Surface的创建、销毁或者改变。其实这一点是通过其内部的静态子接口SurfaceHolder.Callback来实现的。SurfaceHolder.Callback中定义了三个接口方法:
1. abstract void surfaceChanged(SurfaceHolderholder, int format, int width, int height)
当surface发生任何结构性的变化时(格式或者大小),该方法就会被立即调用。
2. abstract void surfaceCreated(SurfaceHolderholder)
当surface对象创建后,该方法就会被立即调用。
3. abstract void surfaceDestroyed(SurfaceHolderholder)
当surface对象在将要销毁前,该方法会被立即调用。
在Android SDK文档中,关于SurfaceView的描述里面,有一段这样的话:
One of the purposes of this class is to provide a surface in which a secondarythread can render into the screen. If you are going to use it this way, youneed to be aware of some threading semantics:
- All SurfaceView and SurfaceHolder.Callbackmethods will be called from the thread running the SurfaceView's window(typically the main thread of the application). They thus need to correctlysynchronize with any state that is also touched by the drawing thread.
- You must ensure that the drawingthread only touches the underlying Surface while it is valid -- betweenSurfaceHolder.Callback.surfaceCreated() andSurfaceHolder.Callback.surfaceDestroyed().
这段话很重要,大致意思如下:
这个类的目的之一,就是提供一个可以用另外一个线程(第二个线程)进行屏幕渲染的surface(译注:即UI线程和绘制线程可以分离)。如果你打算这样使用,那么应当注意一些线程方面的语义:
- 所有SurfaceView和SurfaceHolder.Callback中声明的方法,必须在运行SurfaceView窗口中的线程中调用(典型地,就是应用的主线程。译注:即UI线程),因为它们需要正确地将同时被绘制线程访问的各种状态进行同步。
- 必须保证,只有在背后的Surface有效的时候 – 在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()这两个方法调用之间,访问它。
下面,我们通过一个非常简单例子来实际感受一下(代码摘自http://www.cnblogs.com/xuling/archive/2011/06/06/android.html,并做了一些结构性的改动),请留意代码中的注释:
1. 在Eclipse中创建一个Android Project项目TestSurfaceView,并选择生成缺省的Activity TestSurfaceViewActivity
2. 创建一个绘制线程如下:
3. 自定义一个SurfaceView类如下:
4. 修改TestSurfaceViewActivity.java代码,使之如下:
运行结果:
很显然,我们可以在MyThread的run方法中,做很多更有意思的事情。弄清楚了Surface、SurfaceView、SurfaceHolder和SurfaceHolder.Callback这些概念,以及它们之间的关系,对我们更好地使用它们应该会有相当大的帮助。
先说下标题的意思,因为当时问了一些人,就是这么问的,说不知道啥意思,没理解我想干什么,这个意思就是。。。抽屉难道一定要完全关着或完全开着吗?我只开一部分然后声称我这个抽屉是关着的不行么。
算了,先贴个效果图:
这个情况是抽屉关着的时候:
这个情况是抽屉开着的时候
这是股票的一个抽屉。 然后boss给我说还有3个,顿时纠结,幸亏把内容最为丰富的股票给先搞定了。
。。。。就是这样,一开始就想着把抽屉的手柄写大点,用一个LinearLayout布局来做手柄,但是这样一来,发现布局里面的所有的按钮都相应了我的点击事件,擦。在抽屉手把位置的左边,有个按钮,点击一下会变成减号,但是如果是这样的话,那么我只要一点手柄,他也跟着变了。寻找了一些方法,未果,终于在csdn的一个问题帖子上找到了类似的情况。
基本的思路就是,先拦截了所有抽屉手柄的触摸事件,然后在找出用户想作为手柄的控件id,以及他们的屏幕位置。当点击到那些位置的时候,再做判断。
直接上代码,比较少代码就能实现的,理解起来也不困难,当然,要熟悉Android的控件所对应的实现方法会更简单:
Java代码
public class MySlidingDrawer extends SlidingDrawer {
private int mHandleId = 0; // 抽屉行为控件ID
private int[] mTouchableIds = null; // Handle 部分其他控件ID
public int getHandleId() {
return mHandleId;
}
public void setHandleId(int mHandleId) {
this.mHandleId = mHandleId;
}
public int[] getTouchableIds() {
return mTouchableIds;
}
public void setTouchableIds(int[] mTouchableIds) {
this.mTouchableIds = mTouchableIds;
}
public MySlidingDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MySlidingDrawer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/*
* 获取控件的屏幕区域
*/
public Rect getRectOnScreen(View view) {
Rect rect = new Rect();
int[] location = new int[2];
View parent = view;
if (view.getParent() instanceof View) {
parent = (View) view.getParent();
}
parent.getLocationOnScreen(location);
view.getHitRect(rect);
rect.offset(location[0], location[1]);
return rect;
}
// 拦截触摸事件,用以修改事件
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
// 确定控件的屏幕区域
int[] location = new int[2];
int x = (int) event.getX();
int y = (int) event.getY();
this.getLocationOnScreen(location);
x += location[0];
y += location[1];
// handle部分独立按钮,循环寻找非抽屉手柄的布局。
if (mTouchableIds != null) {
for (int id : mTouchableIds) {
View view = findViewById(id);
Rect rect = getRectOnScreen(view);
if (rect.contains(x, y)) {
Log.i("MySlidingDrawer on touch", String.format(
"Action=%d Button=%s", event.getAction(),
((LinearLayout) view).getContext().toString()));
// return
boolean result = view.dispatchTouchEvent(event);
Log.i("MySlidingDrawer dispatchTouchEvent", "" + result);
return false;
}
}
}
// 抽屉行为控件,本想同上,写成数组,寻找多个手柄,但是这样就没有了抽屉拖动效果
if (event.getAction() == MotionEvent.ACTION_DOWN && mHandleId != 0) {
View view = findViewById(mHandleId);
Log.i("MySlidingDrawer on touch", String.format("%d,%d", x, y));
Rect rect = getRectOnScreen(view);
Log.i("MySlidingDrawer handle screen rect", String
.format("%d,%d %d,%d", rect.left, rect.top, rect.right,
rect.bottom));
if (rect.contains(x, y)) {// 点击抽屉控件时交由系统处理
Log.i("MySlidingDrawer", "Hit handle");
} else {
return false;
}
}
return super.onInterceptTouchEvent(event);
}
// 获取触屏事件
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
大概就是这样的重写。
再来是布局文件:
<?xml version="1.0" encoding="UTF-8"?>
Xml代码
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<Button android:id="@+id/buttontest" android:layout_height="wrap_content"
android:layout_width="wrap_content" android:text="test" />
<!-- bottom menu -->
<!-- 股票类 -->
<com.quan.MySlidingDrawer android:id="@+id/stockDrawer"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:orientation="vertical" android:handle="@+id/handleStock"
android:content="@+id/contentStock" android:visibility="visible">
<!-- top view 3 rows -->
<LinearLayout android:layout_width="fill_parent"
android:id="@+id/handleStock" android:layout_height="85dip"
android:orientation="vertical">
<LinearLayout android:layout_height="wrap_content"
android:layout_width="wrap_content" android:layout_gravity="center">
<ImageButton android:layout_width="60dip" android:id="@+id/mbutton"
android:layout_height="10dip" android:src="/blog_article/@drawable/up/index.html"
android:layout_gravity="center" />
</LinearLayout>
<!-- top left -->
<LinearLayout android:layout_height="75dip"
android:layout_width="fill_parent" android:orientation="horizontal"
android:id="@+id/otherHandler" android:background="#000">
<!-- top left -->
<RelativeLayout android:id="@+id/topLeft"
android:layout_width="39dip" android:layout_height="75dip"
android:orientation="vertical">
<Button android:layout_width="30dip" android:layout_height="30dip"
android:id="@+id/btn_add_cut" android:layout_alignParentTop="true"
android:layout_alignParentLeft="true" android:layout_gravity="center_vertical"
android:focusable="true" android:background="@drawable/add" />
</RelativeLayout>
<RelativeLayout android:layout_width="120dip"
android:layout_height="75dip">
<TextView android:layout_height="50dip" android:text="2616.99"
android:layout_width="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true" android:gravity="right"
android:id="@+id/tvCurrentPrice" android:textColor="#f00"
android:textSize="30dip" android:layout_gravity="center_vertical" />
<TextView android:text="+22.50" android:textColor="#f00"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true" android:id="@+id/tvUpDown" />
<TextView android:layout_height="wrap_content"
android:text="0.21%" android:layout_width="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true" android:gravity="right"
android:textColor="#f00" android:id="@+id/tvAmplitude" />
</RelativeLayout>
<View android:layout_width="1dip" android:layout_height="fill_parent"
android:background="#ff0000" />
<!-- top right -->
<TableLayout android:id="@+id/topRight"
android:layout_width="159dip" android:layout_height="fill_parent"
android:orientation="vertical">
<TableRow>
<TextView android:text="委比" android:textColor="#848684"
android:layout_height="24dip" android:layout_width="70dip"
android:gravity="left" />
<TextView android:id="@+id/tvAppoint" android:text="2.3%"
android:textColor="#f00" android:layout_height="24dip"
android:layout_width="89dip" android:gravity="right"
android:paddingRight="2dip" />
</TableRow>
<View android:layout_height="1dip" android:layout_width="fill_parent"
android:background="#f00" />
<TableRow>
<TextView android:text="委差" android:textColor="#848684"
android:layout_height="24dip" android:layout_width="70dip"
android:gravity="left" />
<TextView android:id="@+id/tvPoorCom" android:text="32"
android:textColor="#f00" android:layout_height="24dip"
android:layout_width="89dip" android:gravity="right"
android:paddingRight="2dip" />
</TableRow>
<View android:layout_height="1dip" android:layout_width="fill_parent"
android:background="#f00" />
<TableRow>
<TextView android:text="量比" android:textColor="#848684"
android:layout_height="24dip" android:layout_width="70dip"
android:gravity="left" />
<TextView android:id="@+id/tvReQuantity" android:text="-2.45"
android:textColor="#0f0" android:layout_height="24dip"
android:layout_width="89dip" android:gravity="right"
android:paddingRight="2dip" />
</TableRow>
</TableLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout android:id="@+id/contentStock"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical" android:background="#000">
<View android:layout_height="1dip" android:layout_width="fill_parent"
android:background="#f00" />
<!-- bottom rows -->
<LinearLayout android:id="@+id/bottomRows"
android:layout_height="253dip" android:layout_width="fill_parent"
android:orientation="horizontal">
<!-- left table -->
<TableLayout android:layout_height="fill_parent"
android:layout_width="160dip">
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="均价" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" android:paddingRight="2dip" />
</TableRow>
<View android:layout_height="1dip" android:background="#f00" />
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="总手" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" android:paddingRight="2dip" />
</TableRow>
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="现手" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:id="@+id/CURVOL" android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" android:paddingRight="2dip" />
</TableRow>
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="金额" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:id="@+id/MONEY" android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" android:paddingRight="2dip" />
</TableRow>
<View android:layout_height="1dip" android:background="#f00" />
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="外盘" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" android:paddingRight="2dip" />
</TableRow>
<View android:layout_height="1dip" android:background="#f00" />
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="买一" android:gravity="left" />
<TextView android:text="2539.82" android:textColor="#f00"
android:layout_height="25dip" android:layout_width="80dip"
android:gravity="center" />
<TextView android:text="23" android:textColor="#AE8B00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" android:paddingRight="2dip" />
</TableRow>
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="买二" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" android:paddingRight="2dip" />
</TableRow>
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="买三" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" android:paddingRight="2dip" />
</TableRow>
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="买四" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" android:paddingRight="2dip" />
</TableRow>
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="买五" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" android:paddingRight="2dip" />
</TableRow>
</TableLayout>
<!-- right table -->
<TableLayout android:layout_height="fill_parent"
android:layout_width="160dip">
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="昨收" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" />
</TableRow>
<View android:layout_height="1dip" android:background="#f00" />
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="开盘" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:id="@+id/OPEN" android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" />
</TableRow>
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="最高" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:id="@+id/MAX" android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" />
</TableRow>
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="最低" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:id="@+id/MIN" android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" />
</TableRow>
<View android:layout_height="1dip" android:background="#f00" />
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="内盘" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:id="@+id/INNER_TRAY" android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" />
</TableRow>
<View android:layout_height="1dip" android:background="#f00" />
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="卖一" android:gravity="left" />
<TextView android:text="2549.82" android:textColor="#f00"
android:layout_height="25dip" android:layout_width="80dip"
android:gravity="center" />
<TextView android:text="45" android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" />
</TableRow>
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="卖二" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" />
</TableRow>
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="卖三" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" />
</TableRow>
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="卖四" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:textColor="#C39C00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" />
</TableRow>
<TableRow>
<TextView android:textColor="#848684"
android:layout_height="25dip" android:layout_width="40dip"
android:text="卖五" android:gravity="left" />
<TextView android:textColor="#f00" android:layout_height="25dip"
android:layout_width="80dip" android:gravity="center" />
<TextView android:textColor="#AE8B00"
android:layout_height="25dip" android:layout_width="40dip"
android:gravity="right" />
</TableRow>
</TableLayout>
</LinearLayout>
</LinearLayout>
</com.quan.MySlidingDrawer>
</RelativeLayout>
做完这些之后又把那三个布局写完,发现。。。你妹的,原来的布局是用一个相对布局来控制抽屉的拉伸长度。其他3个内容都没这么多,所以3个的下面都是空的,又搞笑呢。接着寻找抽屉适配内容进行长度控制。
又是重写,烦躁的很啊。直接代码,主要思想就是先测量里面内容的高度。因为抽屉本身是没有写wrap_content(即适配内容高度来进行拉伸高度控制)的,所以重写的应该是这个部分。在一个英文的编程疑难解答上找到了答案。
onMeasure是测量控件宽高的方法。setMeasuredDimension是设置到布局里~~~
Java代码
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == MeasureSpec.UNSPECIFIED
|| heightSpecMode == MeasureSpec.UNSPECIFIED) {
throw new RuntimeException(
"SlidingDrawer cannot have UNSPECIFIED dimensions");
}
final View handle = getHandle();
final View content = getContent();
measureChild(handle, widthMeasureSpec, heightMeasureSpec);
int extra = handle.getHeight() / 6;
System.out.println(handle.getMeasuredHeight() + " "
+ content.getHeight());
if (mVertical) {
int height = heightSpecSize - handle.getMeasuredHeight()/* + extra */
- mTopOffset;
content.measure(widthMeasureSpec,
MeasureSpec.makeMeasureSpec(height, heightSpecMode));
heightSpecSize = handle.getMeasuredHeight() /* + extra */
+ mTopOffset + content.getMeasuredHeight();
widthSpecSize = content.getMeasuredWidth();
if (handle.getMeasuredWidth() > widthSpecSize)
widthSpecSize = handle.getMeasuredWidth();
}
setMeasuredDimension(widthSpecSize, heightSpecSize);
}
private boolean mVertical;
private int mTopOffset;
直接把这段copy 到需要重写的抽屉类中,测试,搞定。
但是发现左边的那个图标又不能点击了,变成了触摸,就是有一定位移才能改变他的变量。
经过多番努力,改了一下,把那个按钮放在一个布局里面再对布局进行点击监听,一切搞定~~
测试这个抽屉用的activity:
Java代码
public class MainActivity extends Activity {
private MySlidingDrawer mdrawer;// 抽屉
private ImageButton mbutton;// 抽屉拉动按钮
private LinearLayout otherHandler;
private LinearLayout bottomRows;// 抽屉真正的内容
private Button test;
private TextView tv_top;
private Button btn_add_cut;// 真实抽屉左上角按钮
int i = 1;// 定义成员变量,协助点击左上角按钮时改变图标,点击+变成2,点击-变成1
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
findview();
addlistener();
}
/** 初始化组件 */
private void findview() {
tv_top = (TextView) this.findViewById(R.id.tvCurrentPrice);
test = (Button) this.findViewById(R.id.buttontest);
mdrawer = (MySlidingDrawer) findViewById(R.id.stockDrawer);
// 传入抽屉手柄的id
mdrawer.setHandleId(R.id.mbutton);
mdrawer.setTouchableIds(new int[] { R.id.otherHandler });
mbutton = (ImageButton) findViewById(R.id.mbutton);
otherHandler = (LinearLayout) this.findViewById(R.id.otherHandler);
bottomRows = (LinearLayout) findViewById(R.id.bottomRows);
btn_add_cut = (Button) findViewById(R.id.btn_add_cut);
}
/**
* 给各个组建添加事件监听
*/
private void addlistener() {
test.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
tv_top.setText("8585");
}
});
// 抽屉打开
mdrawer.setOnDrawerOpenListener(new MySlidingDrawer.OnDrawerOpenListener() {
public void onDrawerOpened() {
mbutton.setImageResource(R.drawable.down);
}
});
// 抽屉关闭
mdrawer.setOnDrawerCloseListener(new MySlidingDrawer.OnDrawerCloseListener() {
public void onDrawerClosed() {
mbutton.setImageResource(R.drawable.up);
}
});
// 抽屉正在拉动或停止拉动
mdrawer.setOnDrawerScrollListener(new MySlidingDrawer.OnDrawerScrollListener() {
public void onScrollStarted() {
}
public void onScrollEnded() {
}
});
btn_add_cut.setOnClickListener(new ButtonAddCutListener());
//
// 真实抽屉,非头部部分,在打开状态点击时关闭抽屉
bottomRows.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mdrawer.isOpened()) {
mdrawer.animateClose();
}
}
});
// otherHandler.setOnClickListener(new ButtonAddCutListener());
}
/** 抽屉左上角按钮点击改变图片事件 */
private class ButtonAddCutListener implements OnClickListener {
@Override
public void onClick(View v) {
switch (i) {
case 1:
btn_add_cut.setBackgroundDrawable(getResources().getDrawable(
R.drawable.cut));
// btn_add_cut_on.setImageResource(R.drawable.cut);
i = 2;
System.out.println("+");
break;
case 2:
btn_add_cut.setBackgroundDrawable(getResources().getDrawable(
R.drawable.add));
// btn_add_cut_on.setImageResource(R.drawable.add);
i = 1;
System.out.println("-");
break;
}
}
}
}
要求:需要向sd卡中的某个文件夹中的某个txt文件中写入一些用户信息
先不废话,直接上代码:
<!DOCTYPE html> <html> <head> <title>FileWriter Example</title> <script type="text/javascript" charset="utf-8" src="/js/cordova-1.5.0.js"></script> <script type="text/javascript" charset="utf-8"> //等待加载PhoneGap document.addEventListener("deviceready", onDeviceReady, false); // PhoneGap加载完毕 function onDeviceReady() { window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail); } //获取newFile目录,如果不存在则创建该目录 function gotFS(fileSystem) { newFile = fileSystem.root.getDirectory("newFile", {create : true,exclusive : false}, writerFile, fail); } //获取newFile目录下面的dataFile.txt文件,如果不存在则创建此文件 function writerFile(newFile) { newFile.getFile("dataFile.txt", {create : true,exclusive : false}, gotFileEntry, fail); } function gotFileEntry(fileEntry) { fileEntry.createWriter(gotFileWriter, fail); } function gotFileWriter(writer) { writer.onwrite = function(evt) { alert("write success"); }; writer.write("some sample text"); // 文件当前内容是"some sample text" writer.truncate(11); // 文件当前内容是"some sample" writer.seek(4); // 文件当前内容依然是"some sample",但是文件的指针位于"some"的"e"之后 writer.write(" different text"); // 文件的当前内容是"some different text" } function fail(error) { alert("Failed to retrieve file:" + error.code); } // 检索一个已存在的文件,如果该文件不存在时则创建该文件 </script> </head> <body> <h1>Example</h1> <p>Write File</p> </body> </html>
在实现此功能时,是参考phonegap中国上面的API,不过如果完全按照API中介绍的去凑代码的话,则此功能还是比较难实现的。gotFS(fileSystem)与writerFile(newFile)中的两行代码不能写到一块,不然只执行第一行代码。譬如就不能像如下这种方式写:
function gotFS(fileSystem) { newFile = fileSystem.root.getDirectory("newFile", {create : true,exclusive : false}); newFile.getFile("dataFile.txt", {create : true,exclusive : false}, gotFileEntry, fail); }