android 精典博文内容推荐
一、android 精典博文内容推荐
app集成支付宝、app缓存管理、app列表圆角设计、App自动更新之通知栏下载(有续)、索引ListView、App数据格式之解析Json、拖拽ListView http://www.cnblogs.com/qianxudetianxia/category/293007.html
二、Android命名规范(自定义)
此规范参考自android源码,并加以改进,仅供个人使用,如果你觉得还有可取之处,可以参考下,以后有好的规则,再补充
总体规范:
类名要清晰,能反映出这个类的作用,最好能达到见名知义的效果
方法名要使用动宾短语 eg: public boolean moveTaskToBack(boolean nonRoot);
构造函数使用pascal命名规则,又叫大驼峰规则,首字母大写
普通方法和成员变量采用小驼峰规则(camel规则),首字母小写
普通方法的局部变量采用下划线规则,以_开头
1.类的成员变量
所有公开的类常量:定义为静态final类型,名称全部大写 eg: public static final String ACTION_MAIN = "android.intent.action.MAIN";
静态变量:名称以s开头 eg: private static long sInstanceCount = 0;
非静态的私有变量,protected的变量:以m开头 eg: private Intent mIntent;protected ViewParent mParent;
2.方法的命名
方法参数:名称以p开头,表示param的意思 eg: public int getCount(int pCount);
方法内的局部变量以_开头,
eg public int getCount (int pCount){
int _count;
}
三、android ListActivity自定义标题栏
android 自定义标题栏的步骤是
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); // 这里要主要requestWindowFeature和setContentView先后顺序哦 setContentView(R.layout.main); getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title_1); }
但是这个在ListActivity中好像无效,因为ListActivity没有调用setContentView方法,网上的一些解决方法都是在onCreate中调用setContentView,也就是自己实现layout,
有没有更简单的方法呢,使用上面的那样,
其实是有的,在ListActivity中有一个方法很容易被忽略,ensureList(),这是唯一一个设置了layout的地方,但是不是在oncreate中设置的,listActivity没有实现onCreate方法,
继续找,发现在onRestoreInstanceState(), setListAdapter()和getListView()中调用了,也就是说在这3个方法中都有可能会执行setContentView方法,
知道这个就好办了,
解决方法一:在oncreate方法中调用getListView()代替掉上面的setContentView(R.layout.main)
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); //代替掉setContentView getListView(); getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title_1); }
解决方法二:找到你的activity调用setListAdapter的地方,在后面加上面最后一句
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); /** * 你自己的代码 */ setListAdapter(adapter); getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title_1); }
四、Android 设置dialog背景全透明无边框的最简单的方法
做dialog的全透明无边框背景,网上找了n久,都有问题,其实很简单,就两句搞定。
<style name="Translucent_NoTitle" parent="android:style/Theme.Dialog"><item name="android:background">#00000000</item> <!-- 设置自定义布局的背景透明 --><item name="android:windowBackground">@android:color/transparent</item> <!-- 设置window背景透明,也就是去边框 --></style>其他的都可以不用设置,有些属性会继承下来,唯一一个没被继承的是 <item name=" android:windowIsTranslucent ">true</item>,这个不设置也没影响 此方法同样可以用于activity,设置activity半透明
res/values/styles.xml<resources> <style name="Transparent "> <item name="android:windowBackground">@color/transparent_background</item> <item name="android:windowNoTitle">true</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowAnimationStyle">@+android:style/Animation.Translucent</item> </style> </resources>
res/values/color.xml<?xml version="1.0" encoding="utf-8"?> <resources> <color name="transparent_background">#50000000</color> </resources>
注意:color.xml的#5000000前两位是透明的效果参数从00--99(透明--不怎么透明),后6位是颜色的设置
manifest.xml<activity android:name=".TransparentActivity" android:theme="@style/Transparent"> </activity>
五、android layout_gravity失效的问题
相信对于Android的初学者来说,大家都曾经被layout里这两个极其相似的属性迷惑过。
简单使用一下搜索工具,我们就不难找到下面这样的答案:
layout_gravity 表示组件自身在父组件中的位置
gravity 表示组件的子组件在组件中的位置
看似很简单嘛
为什么这么简单的道理,总有同学会发现,在“某些时候”,layout_gravity这个属性不好使了,失去了它应有的作用
问题究竟出在哪里了呢?
当作为父layout的LinearLayout的属性为android:orientation="vertical"的时候,android:layout_gravity="?"这里设为横向的时候才能生效。比如:left,right,center_horizontal等;
当作为父layout的LinearLayout的属性为android:orientation="horizental"的时候,android:layout_gravity="?"这里设为纵向的时候才能生效。比如:top,bottom,center_vertical等;
有一个比较特殊的是center,不管是横向还是纵向的时候,它总有一个方向起作用, 因为LinearLayout他只可能有一个方向,
这nm的,确实让人蛋疼。其实也有点道理吧,就是LinearLayout横向的时候,如果有多个孩子,那就不知道把谁放最右了,
有两个解决方法吧,
(1)用RelativeLayout吧,这个算是费话吧 ,哈哈
(2)在LinearLayout中设置android:gravity这个从官方api的解释是怎么放置它的内容,LinearLayout的内容不就是他的孩子么,问题解决
六、一个神奇的网站http://linux.linuxidc.com/
本文转自http://itindex.net/detail/15843-linux.linuxidc.com-%E8%B5%84%E6%96%99-android
Shared by Yuan
用户名与密码都是 www.linuxidc.com
linux.linuxidc.com - /2011年资料/Android入门教程/
[转到父目录] 2011年7月16日 21:33 <目录> Android 3D 游戏开发教程 2011年8月18日 13:26 <目录> Android WIFI开发介绍 2011年7月25日 16:53 <目录> Android 实战项目之五子棋附源码 2011年8月12日 8:53 <目录> Android 游戏开发之主角的移动与地图的平滑滚动 2011年7月19日 13:24 <目录> Android 的 Remote Service 开发实例 2011年6月18日 9:41 <目录> Android入门教程系列之1——贪吃蛇改进版 2011年8月13日 20:27 <目录> Android反编译工具 2011年7月18日 8:59 274623 Android图形系统的分析与移植.pdf 2011年8月13日 19:59 <目录> Android学习之三步搞定开机启动程序 2011年8月13日 20:13 <目录> Android学习之多点触摸并不神秘 2011年8月13日 19:54 <目录> Android学习之改变Activity切换方式 2011年8月13日 20:10 <目录> Android学习之解析JSON 2011年8月21日 17:09 <目录> Android小闹钟程序源码 2011年7月19日 21:29 <目录> Android应用开发揭秘 2011年8月20日 19:39 <目录> Android开发:巧妙运用ViewStub写出类似Tab选项卡 2011年8月23日 8:00 <目录> Android技术内幕 PDF 2011年7月26日 19:20 <目录> Android框架及应用开发介绍 2011年8月12日 9:10 <目录> Android游戏开发之切换游戏场景特效的实现 2011年8月18日 8:53 <目录> Android游戏开发之单点触摸与多点触摸的响应方式 2011年7月27日 7:59 <目录> Android游戏开发之地图编辑器的使用以及绘制地图 2011年8月14日 6:33 <目录> Android游戏开发之处理按键的响应方式 2011年8月12日 9:26 <目录> Android游戏开发之处理音乐与音效太鼓达人游戏原理 2011年8月22日 14:18 <目录> Android游戏开发之多线程的操作方式源码 2011年8月22日 14:07 <目录> Android游戏开发之小球重力感应实现源码 2011年8月12日 7:58 <目录> Android游戏开发之摄像头的原理以及更新 2011年8月12日 9:01 <目录> Android游戏开发之数据库SQLite 详细介绍 2011年8月12日 8:38 <目录> Android游戏开发之构建游戏框架View与SurFaceView的区别 2011年8月12日 8:33 <目录> Android游戏开发之检测游戏碰撞的原理实现 2011年8月12日 8:14 <目录> Android游戏开发之游戏帧动画的播放与处理 2011年8月12日 8:47 <目录> Android游戏开发之绘制游戏主菜单与进度条加载进度 2011年8月18日 8:58 <目录> Android游戏开发之触摸轨迹曲线的实现处理 2011年8月12日 9:14 <目录> Android游戏开发之飞行射击类游戏原理实现 2011年7月29日 19:40 <目录> Android游戏引擎ANGLE 实例和源码 2011年7月17日 11:24 <目录> Android的binder机制研究(C++部分) 2011年8月6日 8:29 <目录> Android笔记 Application对象的使用-数据传递以及内存泄漏问题 2011年8月6日 8:33 <目录> Android笔记 SQLite总结 ,字带数据库,附记事本,字典小程序 2011年8月18日 13:40 <目录> Android系统中WiFi网络的研究与实现 2011年8月12日 8:28 <目录> Android软件开发之EditText 详解 2011年8月12日 8:41 <目录> Android软件开发之ListView 详解 2011年8月12日 8:05 <目录> Android软件开发之TextView详解 2011年8月12日 9:04 <目录> Android软件开发之应用程序之间的通信介绍 2011年8月12日 8:51 <目录> Android软件开发之数据的新建 储存 读取 删除 2011年7月25日 17:04 <目录> Android软件开发之盘点 2011年8月12日 8:57 <目录> Android软件开发之盘点界面五大布局 2011年6月25日 18:57 <目录> Google Android SDK开发范例大全 2011年8月18日 14:02 <目录> Google Android学习一点通(高清中文版) 2011年6月25日 9:09 <目录> Professional Android Application Development [PDF] 2011年6月28日 15:43 <目录> Ubuntu 11.04 搭建Android开发环境 2011年7月21日 11:40 <目录> 一款Android上小游戏--交互式人机对战五子棋 2011年7月21日 11:54 <目录> 制作可独立分发的Android模拟器 2011年8月16日 8:56 <目录> 基于 Android NDK 的学习之旅-----Java 调用C 2011年8月16日 10:17 <目录> 基于 Android NDK 的学习之旅-----序言 2011年8月17日 15:46 <目录> 基于Android的高效短信查询软件的实现 2011年7月18日 9:00 307448 基于Android的高效短信查询软件的实现.pdf
七、android:layout_weight的巧妙应用(二)
之前我讲过layout_weight的巧妙应用一http://www.cnblogs.com/xiaoQLu/archive/2011/08/08/2130328.html
现在更深入讲下weight的其他应用和原理
先看下图,我要用LinearLayout实现如下效果,2要自适应大小,也就是wrap_content,1要占满剩下的空间怎么办?(当然用相对布局很简单)
如果我要再实现一个更变态点的需求呢,就是2位置要动态变换呢(右图所示效果),根据不同的状态,设置2位置不同控件的隐藏和显示,这种情况下,用RelativeLayout实现就有点麻烦了,你要找到一个基准控件,然后根据他来布局其他控制,如果有一个设置不对,显示的布局就没有这个效果
先分析此问题的难点:
(1)控件2的宽度不确实的,导致1不知道自己的宽度要设为多少。
(2)控件2的位置有多个控件,需要动态控制不同控件来显示和隐藏
如果用LinearLayout就简单很多,linearlayout有一个属性,就是本篇的主角android:layout_weight属性,它是属于LinearLayout特有的。
用LinearyLayout实现有两种写法,都差不多。写提供代码
实现一<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/hello" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/app_name" /></LinearLayout>
实现二<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <Button android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/hello" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/app_name" /></LinearLayout>
其实这两种实现只有一点区别,就是第一个button的layout_weidth,一个是fill_parent,另一个是0dip。其实两种都差不多,下边详细讲解:
首先大家要明白一个概念,就是一个控件(View)本身是可以无限大的,也说是说在它自己的onDraw()函数中,你想要它多大,它就可以有多大,但是为什么我们只能看到一部分呢?
这就要"归功"于android:layout_width和height属性,很多人认为这个是设置控制本身大小的,其实这个理解有偏差,layout_width这个属性不是控制一个子控件它本身的大小(自身大小由它
自己决定),而是父布局提供给这个控件的显示窗口大小,这里叫申请大小(下面同理)。
然后来讲解详细的计算过程,当LinearLayout包含的视图weight>0时,它会mesure两次(这里以上面第一个布局来讲解)
第一次,计算剩余空间,就是用屏幕的宽度减去子控件申请的宽度。设屏幕宽度为TW,第一个button的申请宽度设为x1,weight为w1,第二个button的申请宽度设为x2,weight为w2,剩余空间设为delta
表达示为 delta =TW - (x1+x2)
第二次,分配空间,即父视图最终提供给子控件的显示窗口的大小,
button1的最终显示大小为 x1+delta*w1/(w1+w2),
button2的最终显示大小为 x2+delta*w2/(w1+w2),
结合上面的实例来说明
上面第一个布局,假设手机分辨率为480x320,btn1申请宽度为fill_parent=320,btn2的申请宽度为wrap_content,这个是会调用控件自身的onMeasure计算出来的,假设计算结果为40,那么
第一步剩余空间就是320-(320+40) = -40,
第二步,分配空间,btn1的最终显示空间为320+(-40)*(1/(1+0)) = 240,btn2的最终显示空间为40+(-40*0/(1+0)) = 40
这样就满足了我们开始的那个需求,
再分析第一个布局,其实本质和第一个布局一样,只是把减去变为加上而已
第一步,剩余空间 = 320-(0+40) = 280;
第二步,分配 btn1最终空间 0+280*1/(1+0) = 280,btn2的最终空间为40+280*0/(0+1)
八、HashMap的遍历效率讨论(转载)
本文转自http://hi.baidu.com/injava/item/aac168cd66af7a090bd93a3e
HashMap的遍历效率讨论
经常遇到对HashMap中的key和value值对的遍历操作,有如下两种方法:
Map<String, String[]> paraMap = new HashMap<String, String[]>();
................
//第一个循环
Set<String> appFieldDefIds = paraMap.keySet();
for (String appFieldDefId : appFieldDefIds) {
String[] values = paraMap.get(appFieldDefId);
......
}
//第二个循环
for(Entry<String, String[]> entry : paraMap.entrySet()){
String appFieldDefId = entry.getKey();
String[] values = entry.getValue();
.......
}
第一种实现明显的效率不如第二种实现。
分析如下 Set<String> appFieldDefIds = paraMap.keySet(); 是先从HashMap中取得keySet
代码如下:
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
private class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
public void clear() {
HashMap.this.clear();
}
}
其实就是返回一个私有类KeySet, 它是从AbstractSet继承而来,实现了Set接口。
再来看看for/in循环的语法
for(declaration : expression)
statement
在执行阶段被翻译成如下各式
for(Iterator<E> #i = (expression).iterator(); #i.hashNext();){
declaration = #i.next();
statement
}
因此在第一个for语句for (String appFieldDefId : appFieldDefIds) 中调用了HashMap.keySet().iterator() 而这个方法调用了newKeyIterator()
Iterator<K> newKeyIterator() {
return new KeyIterator();
}
private class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
所以在for中还是调用了
在第二个循环for(Entry<String, String[]> entry : paraMap.entrySet())中使用的Iterator是如下的一个内部类
private class EntryIterator extends HashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() {
return nextEntry();
}
}
此时第一个循环得到key,第二个循环得到HashMap的Entry
效率就是从循环里面体现出来的第二个循环此致可以直接取key和value值
而第一个循环还是得再利用HashMap的get(Object key)来取value值
现在看看HashMap的get(Object key)方法
public V get(Object key) {
Object k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length); //Entry[] table
Entry<K,V> e = table[i];
while (true) {
if (e == null)
return null;
if (e.hash == hash && eq(k, e.key))
return e.value;
e = e.next;
}
}
其实就是再次利用Hash值取出相应的Entry做比较得到结果,所以使用第一中循环相当于两次进入HashMap的Entry中
而第二个循环取得Entry的值之后直接取key和value,效率比第一个循环高。
其实按照Map的概念来看也应该是用第二个循环好一点,它本来就是key和value的值对,将key和value分开操作在这里不是个好选择。
九、listview反弹实现详解
本文转自http://jianwang0412.iteye.com/blog/1267885
重写listview,通过监听滑动事件,根据滑动时所处的位置,以及滑动的方向,使用view的内置scrollTo或scrollBy函数来移动view到你手势互动的距离(此处为一半),然后当确定消费了给事件后,又回滚到(0,0)点。当然只有在超出了边界时才回滚。而且回滚的过程由TranslateAnimation来控制,这样的好处在代码的解释中。我是基于网络上的listviewpress改了一些(有几处好像是被篡改了,我又按我的理解将它改正过来,运行后没问题)。一下是关键的代码,整个代码见附件中。有不懂的可以问问,大家互相学习。
- packagecom.listview.test;
- importandroid.content.Context;
- importandroid.graphics.Rect;
- importandroid.util.AttributeSet;
- importandroid.util.Log;
- importandroid.view.GestureDetector;
- importandroid.view.MotionEvent;
- importandroid.view.View;
- importandroid.view.GestureDetector.OnGestureListener;
- importandroid.view.animation.TranslateAnimation;
- importandroid.widget.ListView;
- publicclassCustomerListViewextendsListView{
- privateContextmContext;
- privatebooleanoutBound=false;
- privateintdistance;
- privateintfirstOut;
- publicCustomerListView(Contextc){
- super(c);
- this.mContext=c;
- }
- publicCustomerListView(Contextc,AttributeSetattrs){
- super(c,attrs);
- this.mContext=c;
- }
- publicCustomerListView(Contextc,AttributeSetattrs,intdefStyle){
- super(c,attrs,defStyle);
- this.mContext=c;
- }
- GestureDetectorgestureDetector=newGestureDetector(
- newOnGestureListener(){
- publicbooleanonDown(MotionEvente){
- //TODOAuto-generatedmethodstub
- returnfalse;
- }
- publicbooleanonFling(MotionEvente1,MotionEvente2,
- floatvelocityX,floatvelocityY){
- //TODOAuto-generatedmethodstub
- returnfalse;
- }
- publicvoidonLongPress(MotionEvente){
- //TODOAuto-generatedmethodstub
- }
- /**捕捉滑动事件e1为此处为的ACTION_DOWN事件(无论什么动作,起始都是该动作),而e2是触发调用onScroll的事件。而在此期间,可能已经
- *触发了多次的onScroll,因为我们滑动过程可能比较长,一旦长于某个值,就会触发一次(即一个Move应该是由多个
- *move事件组成的,开头当然是个ACTION_DOWN事件),也就会发出一个移动的MotionEvent。但是期间开始此次
- *scroll的e1是唯一的。而distance是最近一次调用onScroll以来的距离(前一个e2和现在e2的距离:比如上次是-30,这次是-60(比如向下拉),
- *那么distanceY=-60-(-30)=-30)。
- */
- publicbooleanonScroll(MotionEvente1,MotionEvente2,
- floatdistanceX,floatdistanceY){
- /**
- *firstPos和lastPos是adapter中元素的Id
- */
- intfirstPos=getFirstVisiblePosition();
- intlastPos=getLastVisiblePosition();
- intitemCount=getCount();
- /**
- *滑出边界,而且是一个极点,即可视部分已经已经不存在了,那么直接回到原点
- */
- if(outBound&&firstPos!=0&&lastPos!=(itemCount-1)){
- scrollTo(0,0);
- returnfalse;
- }
- /**
- *getChildAt是屏幕上可见的元素的id,比如现在屏幕上可见的是adapter中的
- *4号到10号,那么你调用getChildAt应该是0~6号
- *listView.getChildAt(i)workswhere0istheveryfirstvisiblerowand
- *(n-1)isthelastvisiblerow(wherenisthenumberofvisibleviewsyousee).
- *进入该onScroll有4种可能,第一种是刚开始的时候,此时firstPos==0,而且可视的item在getChildAt的
- *返回也是第一个元素,即adapter元素的index和可视的view的编号一致,所以firstview不为空(lastview也一样)。
- *当你向上滑动时,distanceY是大于0的。此时将不消费此次事件,那么将正常地在没有超出边际出滚动。
- *第二种是,若以上是向下拉,那么应该属于超出范围的情况,则要消费此时事件。
- *第三种和第一种类似,只是到了当刚好显示最后一个item时,显然firstView和lastView都将是null,因为
- *此时的adapter的index和getChildAt的index不是相等的,而是成对应关系,
- *即index_adp-firstPos=index_getChild,此时你若使用getChildAt(firstpos-firstpos),那返回的
- *将是非null。同理在lastView。第四种是当在第三情况下,向上拉,那么属于超出边界。那么lastView是null这个特征
- *将可以判断是否进入了下临界区。
- *总结以上四种情况,每当触发临界区时(dispatchTouchEvent时getFirstVisiblePosition()==0
- *和getLastVisiblePosition()==getCount()-1),就可以通过distanceY的方向性判断是正常的滑动
- *还是将要滑出临界区。若是滑出临界区,说明此次将消费该事件,所以返回true,那么在dispatchTouchEvent
- *将设置outBand为true,那么第二次再进入时,将可以通过outBand来确定是否出了临界区。
- *
- *带方向的函数:onScrollBy/To和onScroll
- */
- ViewfirstView=getChildAt(firstPos);
- ViewlastView=getChildAt(lastPos-1);
- /**
- *记录下第一次的e2的y轴距离,此次过后outBound就变为了true。这样distance就是跟踪最近的一次e2
- *和最开始一次的e2的距离。
- */
- if(!outBound){
- firstOut=(int)e2.getRawY();
- }
- if(firstView!=null
- &&(outBound||(firstPos==0
- &&firstView.getTop()==0&&distanceY<0))){
- distance=(int)(firstOut-e2.getRawY());//此处应为负值,即view向下滑动
- /**
- *scrollBy中的值带有方向,x若为正,则应该以view中该x点显示在新的原点上,即拿新的点去
- <spanstyle="white-space:pre;"></span>*重合y轴,就好像整个布局被往左拉动。
- *y为正,则向上滑动|y|距离。负则相反。
- */
- scrollBy(0,distance/2);
- Log.v("onScroll","e2.getRawY():"+e2.getRawY());
- Log.v("onScroll","distance:"+distance);
- Log.v("onScroll","distanceY:"+distanceY);
- returntrue;
- }
- if(lastView==null&&(outBound||(lastPos==itemCount-1&&distanceY>0))){
- Log.d("bottom","bottom");
- distance=(int)(firstOut-e2.getRawY());//此处应为正直,因为view向上滑动
- scrollBy(0,distance/2);
- returntrue;
- }
- returnfalse;
- }
- publicvoidonShowPress(MotionEvente){
- //TODOAuto-generatedmethodstub
- }
- publicbooleanonSingleTapUp(MotionEvente){
- //TODOAuto-generatedmethodstub
- returnfalse;
- }
- });
- /**
- *最早响应触屏事件,按下和释放响应两次
- */
- publicbooleandispatchTouchEvent(MotionEventev){
- if(getFirstVisiblePosition()==0){
- intact=ev.getAction();
- if((act==MotionEvent.ACTION_UP||act==MotionEvent.ACTION_CANCEL)
- &&outBound){
- outBound=false;
- }
- if(!gestureDetector.onTouchEvent(ev)){
- outBound=false;
- }else{
- outBound=true;
- }
- Rectrect=newRect();
- getLocalVisibleRect(rect);
- /**
- *rect.top是个正的距离值,而TanslateAnimation填的是坐标值(有方向的);
- */
- TranslateAnimationam=newTranslateAnimation(0,0,-rect.top,0);
- /**
- *若此处时间设为0,将导致一阵的抖动,因为完成回滚的速度不是分步,而是直接到终点
- *因为每次触发onScroll时都会做一次回滚,而当传进又一次move时,上一次的move还没作完
- *就将被新的一次覆盖,所以不用担心产生抖动。所以此处给它设时间就是抓住它需要时间来完成回滚的目标,相当
- *于给它一个时间的缓冲来实现移动,因为当你在移动时,实际是不需要回滚的,只有你释放了手指还才需要回滚。
- *注意,此时调用scrollTo已经将位置返回了0(可以把animation当成是模型,只有使用scrollTo才
- *能真正触发该移动,结果是已经知道了的,即移动到原点,而过程是TranslateAnimation参谋的,即
- *scrollTo在移动时会调用onScrollChange来实际移动,而onScrollChange则根据传入的参数来移动
- *而TranslateAnimation则可以控制该参数。可以把scrollTo先去掉,就可以发现newtop和
- *afterscrollBy是一样的值)。也就是newTop=0。所以每次迭代相减都是现在的e2减去最初的e2在y轴上的值,
- *这样通过scrollBy就可以将view移动到新的位置,而此时top也就又被写成了新的滑动的位置(是滑动距离的一半位置)。
- *11-1923:51:11.101:V/onScroll(18396):afterscrollBytop:0
- 11-1923:51:11.101:V/onScroll(18396):newtop:0
- 11-1923:51:11.249:V/onScroll(18396):afterscrollBytop:0
- 11-1923:51:11.249:V/onScroll(18396):newtop:0
- 11-1923:51:11.288:V/onScroll(18396):afterscrollBytop:-6
- 11-1923:51:11.288:V/onScroll(18396):newtop:0
- 11-1923:51:11.319:V/onScroll(18396):afterscrollBytop:-16
- 11-1923:51:11.319:V/onScroll(18396):newtop:0
- 11-1923:51:11.358:V/onScroll(18396):afterscrollBytop:-20
- 11-1923:51:11.358:V/onScroll(18396):newtop:0
- 11-1923:51:11.374:V/onScroll(18396):afterscrollBytop:-27
- 11-1923:51:11.374:V/onScroll(18396):newtop:0
- */
- am.setDuration(300);
- startAnimation(am);
- Log.v("onScroll","afterscrollBytop:"+rect.top);
- scrollTo(0,0);
- getLocalVisibleRect(rect);
- Log.v("onScroll","newtop:"+rect.top);
- }
- Log.d("getLastVisiblePosition()",getLastVisiblePosition()+"");
- Log.d("getCount()",getCount()+"");
- if(getLastVisiblePosition()==getCount()-1){
- intact=ev.getAction();
- if((act==MotionEvent.ACTION_DOWN||act==MotionEvent.ACTION_CANCEL)
- &&outBound){
- outBound=false;
- }
- if(!gestureDetector.onTouchEvent(ev)){
- outBound=false;
- }else{
- outBound=true;
- }
- if(outBound){
- Rectrect1=newRect();
- getLocalVisibleRect(rect1);
- TranslateAnimationam1=newTranslateAnimation(0,0,rect1.top,0);
- am1.setDuration(300);
- startAnimation(am1);
- scrollTo(0,0);
- }
- }
- returnsuper.dispatchTouchEvent(ev);
- };
- }
listviewPress.zip(82.6 KB)
十、Android SAX解析之错误纠正!!
本文转自http://blog.csdn.net/feng88724/article/details/7013675
在讲这次错误之前,先看一下下面这段代码。 【◆以下解析方法是错误的×】
[java] view plain copy print ?- importjava.util.ArrayList;
- importjava.util.List;
- importorg.xml.sax.Attributes;
- importorg.xml.sax.SAXException;
- importorg.xml.sax.helpers.DefaultHandler;
- importandroid.util.Log;
- publicclassXmlHandlerextendsDefaultHandler{
- privatefinalStringTAG=this.getClass().getSimpleName();
- /**XML文件中标签定义*/
- privatefinalStringTAG_Article="Article";
- privatefinalStringTAG_ArticleID="ArticleID";
- privatefinalStringTAG_Title="Title";
- privatefinalStringTAG_Date="Date";
- privatefinalStringTAG_SmallPictures="SmallPictures";
- privatefinalStringTAG_LargePictures="LargePictures";
- privatefinalStringTAG_Category="Category";
- privatestaticfinalStringTAG_HeadNote="HeadNote";
- privatestaticfinalStringTAG_SubTitle="SubTitle";
- privatestaticfinalStringTAG_Source="Source";
- //当前正在解析的TAG
- privateStringcurrentName;
- //单个文章
- privateNewsnews=null;
- //文章列表
- privateList<News>newsList=null;
- //解析开始时间
- privatelongstart_time;
- privatebooleanflag=false;
- @Override
- publicvoidcharacters(char[]ch,intstart,intlength)
- throwsSAXException{
- super.characters(ch,start,length);
- if(!flag){
- return;
- }
- //取值
- Stringvalue=newString(ch,start,length);
- Log.d(TAG,"Element:"+currentName+"ElementValue:"+value);
- if(value!=null){
- if(TAG_ArticleID.equals(currentName)){
- news.setArticleId(value);
- }elseif(TAG_Title.equals(currentName)){
- news.setTitle(value);
- }elseif(TAG_Date.equals(currentName)){
- news.setDate(value);
- }elseif(TAG_Category.equals(currentName)){
- news.setCategory(value);
- }elseif(TAG_SmallPictures.equals(currentName)){
- news.setSmallPicture(value);
- }elseif(TAG_LargePictures.equals(currentName)){
- news.setLargePicture(value);
- }elseif(TAG_HeadNote.equals(currentName)){
- news.setHeadNote(value);
- }elseif(TAG_SubTitle.equals(currentName)){
- news.setSubTitle(value);
- }elseif(TAG_Source.equals(currentName)){
- news.setSource(value);
- }
- }
- }
- @Override
- publicvoidstartDocument()throwsSAXException{
- super.startDocument();
- start_time=System.currentTimeMillis();
- newsList=newArrayList<News>();
- }
- @Override
- publicvoidstartElement(Stringuri,StringlocalName,StringqName,Attributesattributes)throwsSAXException{
- super.startElement(uri,localName,qName,attributes);
- this.currentName=localName;
- flag=true;
- if(TAG_Article.equals(localName)){
- news=newNews();
- }
- }
- @Override
- publicvoidendElement(Stringuri,StringlocalName,StringqName)
- throwsSAXException{
- super.endElement(uri,localName,qName);
- flag=false;
- if(TAG_Article.equals(localName)){
- newsList.add(news);
- }
- }
- @Override
- publicvoidendDocument()throwsSAXException{
- super.endDocument();
- longend=System.currentTimeMillis();
- Log.d(TAG,"ParseList'sXmlCost:"+(end-start_time)+"!!");
- }
- }
Baidu 或者 Google 一下 “Android Sax 解析” , 给出的Sample无一例外都是如此。 坑爹啊... 甚至连有些书籍中都是这么写的, 比如《Android开发入门与实践》。(本书亲自确认过,其他书情况不详)
没错, 一般情况下,这么写是可以的, 而且在大多数情况下解析出来也是正确的。 但是就是偶尔会出错, 这个时候通常你都莫不着头脑, 怎么回事? 数据没错啊,解析部分代码貌似也没问题.. 真是奇了怪了。 其实问题都出在上面那段代码上!!
大家都认为 SAX 解析过程大致如下:
startDocument -> startElement ->characters ->endElement ->endDocument
没错,就是这样,startElement 读取起始标签,endElement 读取结束标签,characters 呢?当然是读取其值, 这没错,但是大家都天真的以为characters 只执行一次,并且一次就读取了全部内容。错就错在这!
其实characters 是很有可能会执行多次的,当遇到内容中有回车,\t等等内容时,它很有可能就执行多次。 有的人可能会说,那我没有这些是不是就只执行一次了? 看下我实测结果:
测试用XML如下:
[html] view plain copy print ?- <News>
- <Article>
- <ArticleID>1000555</ArticleID>
- <Title><![CDATA[郑州“亚洲第一桥”通车6年成危桥]]></Title>
- <Date>2011-11-2514:23:52</Date>
- <SmallPictures>livenews/images/s20.png</SmallPictures>
- <LargePictures>livenews/images/l20.png</LargePictures>
- <Category>闻天下</Category>
- <HeadNote></HeadNote>
- <SubTitle></SubTitle>
- <Author></Author>
- <Source>人民日报</Source>
- <Abstract></Abstract>
- </Article>
- <Article>
- <ArticleID>1000554</ArticleID>
- <Title><![CDATA[内地事业单位拟设统一工资制度]]></Title>
- <Date>2011-11-2514:22:33</Date>
- <Category><![CDATA[闻天下]]></Category>
- <HeadNote></HeadNote>
- <SubTitle></SubTitle>
- <Author></Author>
- <Source></Source>
- <Abstract></Abstract>
- </Article>
- <Article>
- <ArticleID>1000553</ArticleID>
- <Title></Title>
- <Date>2011-11-2514:21:23</Date>
- <SmallPictures>livenews/images/s21.png</SmallPictures>
- <LargePictures>livenews/images/l21.png</LargePictures>
- <Category><![CDATA[星娱乐]]></Category>
- <HeadNote></HeadNote>
- <SubTitle></SubTitle>
- <Author></Author>
- <Source><![CDATA[凤凰网综合]]></Source>
- <Abstract></Abstract>
- </Article>
- <News>
可以很明显的看到,在解析 <ArticleID>1000553</ArticleID> 这一段时,characters执行了两次,将内容"1000553"分两次读取.. 用上面那种方式的最终结果就是ArticleID = 00553 了。 那如果你的应用需要根据这个id 进一步获取内容岂不是死翘翘了?(比如这边根据id获取新闻详细内容)
好了,废话不多说了,看下正确的写法! 【★以下解析方法才是正确的 √ 】
[java] view plain copy print ?- importjava.util.ArrayList;
- importjava.util.List;
- importorg.xml.sax.Attributes;
- importorg.xml.sax.SAXException;
- importorg.xml.sax.helpers.DefaultHandler;
- importandroid.util.Log;
- publicclassXmlHandlerextendsDefaultHandler{
- privatefinalStringTAG=this.getClass().getSimpleName();
- /**XML文件中标签定义*/
- privatefinalStringTAG_Article="Article";
- privatefinalStringTAG_ArticleID="ArticleID";
- privatefinalStringTAG_Title="Title";
- privatefinalStringTAG_Date="Date";
- privatefinalStringTAG_SmallPictures="SmallPictures";
- privatefinalStringTAG_LargePictures="LargePictures";
- privatefinalStringTAG_Category="Category";
- privatestaticfinalStringTAG_HeadNote="HeadNote";
- privatestaticfinalStringTAG_SubTitle="SubTitle";
- privatestaticfinalStringTAG_Source="Source";
- //单个文章
- privateNewsnews=null;
- //文章列表
- privateList<News>newsList=null;
- //解析开始时间
- privatelongstart_time;
- //(1)
- privateStringBuildersb=newStringBuilder();
- @Override
- publicvoidcharacters(char[]ch,intstart,intlength)
- throwsSAXException{
- super.characters(ch,start,length);
- //(2)不管在startElement到endElement的过程中,执行了多少次characters,都会将内容添加到StringBuilder中,不会丢失内容
- sb.append(ch,start,length);
- }
- @Override
- publicvoidstartDocument()throwsSAXException{
- super.startDocument();
- start_time=System.currentTimeMillis();
- newsList=newArrayList<News>();
- }
- @Override
- publicvoidstartElement(Stringuri,StringlocalName,StringqName,Attributesattributes)throwsSAXException{
- super.startElement(uri,localName,qName,attributes);
- //(3)开始收集新的标签的数据时,先清空历史数据
- sb.setLength(0);
- if(TAG_Article.equals(localName)){
- news=newNews();
- }
- }
- @Override
- publicvoidendElement(Stringuri,StringlocalName,StringqName)
- throwsSAXException{
- super.endElement(uri,localName,qName);
- //(4)原来在characters中取值,现改在此取值
- Stringvalue=sb.toString();
- if(TAG_ArticleID.equals(localName)){
- news.setArticleId(value);
- }elseif(TAG_Title.equals(localName)){
- news.setTitle(value);
- }elseif(TAG_Date.equals(localName)){
- news.setDate(value);
- }elseif(TAG_Category.equals(localName)){
- news.setCategory(value);
- }elseif(TAG_SmallPictures.equals(localName)){
- news.setSmallPicture(value);
- }elseif(TAG_LargePictures.equals(localName)){
- news.setLargePicture(value);
- }elseif(TAG_HeadNote.equals(localName)){
- news.setHeadNote(value);
- }elseif(TAG_SubTitle.equals(localName)){
- news.setSubTitle(value);
- }elseif(TAG_Source.equals(localName)){
- news.setSource(value);
- }
- if(TAG_Article.equals(localName)){
- newsList.add(news);
- }
- }
- @Override
- publicvoidendDocument()throwsSAXException{
- super.endDocument();
- longend=System.currentTimeMillis();
- Log.d(TAG,"ParseList'sXmlCost:"+(end-start_time)+"!!");
- }
- }
归纳为三点:
1.startElement的时候,new StringBuilder(); 或者sb.setLength(0); (我建议后者)
2.characters的时候,sb.append(ch, start, length);
3.endElement的时候,sb.toString(); 此时StringBuilder中的内容才是解析的结果
通过这种方法就不会再有数据离奇丢失的情况了(同时也不需要像错误方法那样再设个currentTag之类的了,逻辑繁杂了,还出错)!
希望大家可以尽早看到这篇文章,不要继续被吭了!!!
更多相关文章
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用
- python list.sort()根据多个关键字排序的方法实现
- android上一些方法的区别和用法的注意事项
- android EditText设置不可写
- Android(安卓)拨号器的简单实现
- android实现字体闪烁动画的方法
- Android中dispatchDraw分析
- Android中不同应用间实现SharedPreferences数据共享