android 精典博文内容推荐

本文汇总了Android开发中实用的技巧,包括博文推荐、命名规范、自定义标题栏、透明Dialog设置、LinearLayout布局技巧等,帮助开发者提高开发效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、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改了一些(有几处好像是被篡改了,我又按我的理解将它改正过来,运行后没问题)。一下是关键的代码,整个代码见附件中。有不懂的可以问问,大家互相学习。

  1. package com.listview.test;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Rect;  
  5. import android.util.AttributeSet;  
  6. import android.util.Log;  
  7. import android.view.GestureDetector;  
  8. import android.view.MotionEvent;  
  9. import android.view.View;  
  10. import android.view.GestureDetector.OnGestureListener;  
  11. import android.view.animation.TranslateAnimation;  
  12. import android.widget.ListView;  
  13.   
  14. public class CustomerListView extends ListView {  
  15.   
  16.     private Context mContext;  
  17.     private boolean outBound = false;  
  18.     private int distance;  
  19.     private int firstOut;  
  20.   
  21.     public CustomerListView(Context c) {  
  22.         super(c);  
  23.         this.mContext = c;  
  24.     }  
  25.   
  26.     public CustomerListView(Context c, AttributeSet attrs) {  
  27.         super(c, attrs);  
  28.         this.mContext = c;  
  29.     }  
  30.   
  31.     public CustomerListView(Context c, AttributeSet attrs, int defStyle) {  
  32.         super(c, attrs, defStyle);  
  33.         this.mContext = c;  
  34.     }  
  35.   
  36.     GestureDetector gestureDetector = new GestureDetector(  
  37.             new OnGestureListener() {  
  38.   
  39.                 public boolean onDown(MotionEvent e) {  
  40.                     // TODO Auto-generated method stub  
  41.                     return false;  
  42.                 }  
  43.   
  44.                 public boolean onFling(MotionEvent e1, MotionEvent e2,  
  45.                         float velocityX, float velocityY) {  
  46.                     // TODO Auto-generated method stub  
  47.                     return false;  
  48.                 }  
  49.   
  50.                 public void onLongPress(MotionEvent e) {  
  51.                     // TODO Auto-generated method stub  
  52.   
  53.                 }  
  54. /**捕捉滑动事件  e1为此处为的ACTION_DOWN事件(无论什么动作,起始都是该动作),而e2是触发调用onScroll的事件。而在此期间,可能已经 
  55.  * 触发了多次的onScroll,因为我们滑动过程可能比较长,一旦长于某个值,就会触发一次(即一个Move应该是由多个 
  56.  * move事件组成的,开头当然是个ACTION_DOWN事件),也就会发出一个移动的MotionEvent。但是期间开始此次 
  57.  * scroll的e1是唯一的。而distance是最近一次调用onScroll以来的距离(前一个e2和现在e2的距离:比如上次是-30,这次是-60(比如向下拉), 
  58.  * 那么 distanceY=-60-(-30)=-30)。 
  59.  */  
  60.   
  61.                 public boolean onScroll(MotionEvent e1, MotionEvent e2,  
  62.                         float distanceX, float distanceY) {  
  63.                     /** 
  64.                      * firstPos和lastPos是adapter中元素的Id 
  65.                      */  
  66.                     int firstPos = getFirstVisiblePosition();  
  67.                     int lastPos = getLastVisiblePosition();  
  68.                     int itemCount = getCount();  
  69.                       
  70.                     /** 
  71.                      * 滑出边界,而且是一个极点,即可视部分已经已经不存在了,那么直接回到原点 
  72.                      */  
  73.                     if (outBound && firstPos != 0 && lastPos != (itemCount - 1)) {  
  74.                         scrollTo(00);  
  75.                         return false;  
  76.                     }  
  77.                     /** 
  78.                      * getChildAt是屏幕上可见的元素的id,比如现在屏幕上可见的是adapter中的 
  79.                      * 4号到10号,那么你调用getChildAt应该是0~6号  
  80.                      * listView.getChildAt(i) works where 0 is the very first visible row and 
  81.                      *  (n-1) is the last visible row (where n is the number of visible views you see). 
  82.                      *  进入该onScroll有4种可能,第一种是刚开始的时候,此时firstPos==0,而且可视的item在getChildAt的 
  83.                      *  返回也是第一个元素,即adapter元素的index和可视的view的编号一致,所以firstview不为空(lastview也一样)。 
  84.                      *  当你向上 滑动时,distanceY是大于0的。此时将不消费此次事件,那么将正常地在没有超出边际出滚动。 
  85.                      *  第二种是,若以上是向下拉,那么应该属于超出范围的情况,则要消费此时事件。 
  86.                      *  第三种和第一种类似,只是到了当刚好显示最后一个item时,显然firstView和lastView都将是null,因为 
  87.                      *  此时的adapter的index和getChildAt的index不是相等的,而是成对应关系, 
  88.                      *  即index_adp-firstPos=index_getChild,此时你若使用getChildAt(firstpos-firstpos),那返回的 
  89.                      *  将是非null。同理在lastView。第四种是当在第三情况下,向上拉,那么属于超出边界。那么lastView是null这个特征 
  90.                      *  将可以判断是否进入了下临界区。 
  91.                      *  总结以上四种情况,每当触发临界区时(dispatchTouchEvent时getFirstVisiblePosition()==0 
  92.                      *  和getLastVisiblePosition()==getCount()-1),就可以通过distanceY的方向性判断是正常的滑动 
  93.                      *  还是将要滑出临界区。若是滑出临界区,说明此次将消费该事件,所以返回true,那么在dispatchTouchEvent 
  94.                      *  将设置outBand为true,那么第二次再进入时,将可以通过outBand来确定是否出了临界区。 
  95.                      *   
  96.                      *  带方向的函数:onScrollBy/To和onScroll 
  97.                      */  
  98.                     View firstView = getChildAt(firstPos);  
  99.                     View lastView = getChildAt(lastPos-1);  
  100.                 /** 
  101.                  * 记录下第一次的e2的y轴距离,此次过后outBound就变为了true。这样distance就是跟踪最近的一次e2 
  102.                  * 和最开始一次的e2的距离。 
  103.                  */  
  104.                     if (!outBound) {  
  105.                         firstOut = (int) e2.getRawY();  
  106.                     }  
  107.                     if (firstView != null  
  108.                             && (outBound || (firstPos == 0  
  109.                                     && firstView.getTop() == 0 && distanceY < 0))) {  
  110.                         distance = (int) (firstOut - e2.getRawY());//此处应为负值,即view向下滑动  
  111.                 /**  
  112.                  * scrollBy中的值带有方向,x若为正,则应该以view中该x点显示在新的原点上,即拿新的点去  
Java代码   收藏代码
  1. <span style="white-space: pre;">                </span>*重合y轴,就好像整个布局被往左拉动。  
  2.                  * y为正,则向上滑动|y|距离。负则相反。  
  3.                  */  
  4.                         scrollBy(0, distance / 2);  
  5.                         Log.v("onScroll""e2.getRawY():"+e2.getRawY());  
  6.                         Log.v("onScroll""distance:"+distance);  
  7.                         Log.v("onScroll""distanceY:"+distanceY);  
  8.                         return true;  
  9.                     }  
  10.                     if (lastView == null&&(outBound || (lastPos == itemCount - 1 && distanceY > 0))) {  
  11.                         Log.d("bottom""bottom");  
  12.                         distance = (int) (firstOut - e2.getRawY());//此处应为正直,因为view向上滑动  
  13.                         scrollBy(0, distance/2);  
  14.                         return true;  
  15.                     }  
  16.                     return false;  
  17.                 }  
  18.   
  19.                 public void onShowPress(MotionEvent e) {  
  20.                     // TODO Auto-generated method stub  
  21.   
  22.                 }  
  23.   
  24.                 public boolean onSingleTapUp(MotionEvent e) {  
  25.                     // TODO Auto-generated method stub  
  26.                     return false;  
  27.                 }  
  28.             });  
  29.   
  30.     /** 
  31.      * 最早响应触屏事件,按下和释放响应两次 
  32.      */  
  33.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  34.         if(getFirstVisiblePosition()==0){  
  35.             int act = ev.getAction();  
  36.             if ((act == MotionEvent.ACTION_UP || act == MotionEvent.ACTION_CANCEL)  
  37.                     && outBound) {  
  38.                 outBound = false;  
  39.             }  
  40.             if (!gestureDetector.onTouchEvent(ev)) {  
  41.                 outBound = false;  
  42.             } else {  
  43.                 outBound = true;  
  44.             }  
  45.             Rect rect = new Rect();  
  46.             getLocalVisibleRect(rect);  
  47.             /** 
  48.              * rect.top是个正的距离值,而TanslateAnimation填的是坐标值(有方向的); 
  49.              */  
  50.             TranslateAnimation am = new TranslateAnimation(00, -rect.top, 0);  
  51.             /** 
  52.              * 若此处时间设为0,将导致一阵的抖动,因为完成回滚的速度不是分步,而是直接到终点 
  53.              * 因为每次触发onScroll时都会做一次回滚,而当传进又一次move时,上一次的move还没作完 
  54.              * 就将被新的一次覆盖,所以不用担心产生抖动。所以此处给它设时间就是抓住它需要时间来完成回滚的目标,相当 
  55.              * 于给它一个时间的缓冲来实现移动,因为当你在移动时,实际是不需要回滚的,只有你释放了手指还才需要回滚。 
  56.              * 注意,此时调用scrollTo已经将位置返回了0(可以把animation当成是模型,只有使用scrollTo才 
  57.              * 能真正触发该移动,结果是已经知道了的,即移动到原点,而过程是TranslateAnimation参谋的,即 
  58.              * scrollTo在移动时会调用onScrollChange来实际移动,而onScrollChange则根据传入的参数来移动 
  59.              * 而TranslateAnimation则可以控制该参数。可以把scrollTo先去掉,就可以发现new top 和 
  60.              * after scrollBy是一样的值)。也就是new Top=0。所以每次迭代相减都是现在的e2减去最初的e2在y轴上的值, 
  61.              * 这样通过scrollBy就可以将view移动到新的位置,而此时top也就又被写成了新的滑动的位置(是滑动距离的一半位置)。 
  62.              * 11-19 23:51:11.101: V/onScroll(18396): after scrollBy top:0 
  63.                 11-19 23:51:11.101: V/onScroll(18396): new top:0 
  64.                 11-19 23:51:11.249: V/onScroll(18396): after scrollBy top:0 
  65.                 11-19 23:51:11.249: V/onScroll(18396): new top:0 
  66.                 11-19 23:51:11.288: V/onScroll(18396): after scrollBy top:-6 
  67.                 11-19 23:51:11.288: V/onScroll(18396): new top:0 
  68.                 11-19 23:51:11.319: V/onScroll(18396): after scrollBy top:-16 
  69.                 11-19 23:51:11.319: V/onScroll(18396): new top:0 
  70.                 11-19 23:51:11.358: V/onScroll(18396): after scrollBy top:-20 
  71.                 11-19 23:51:11.358: V/onScroll(18396): new top:0 
  72.                 11-19 23:51:11.374: V/onScroll(18396): after scrollBy top:-27 
  73.                 11-19 23:51:11.374: V/onScroll(18396): new top:0 
  74.              */  
  75.             am.setDuration(300);  
  76.             startAnimation(am);  
  77.             Log.v("onScroll","after scrollBy top:"+rect.top);  
  78.             scrollTo(00);  
  79.             getLocalVisibleRect(rect);  
  80.             Log.v("onScroll""new top:"+rect.top);  
  81.         }  
  82.         Log.d("getLastVisiblePosition()", getLastVisiblePosition()+"");  
  83.         Log.d("getCount()", getCount()+"");  
  84.         if(getLastVisiblePosition()==getCount()-1){  
  85.             int act = ev.getAction();  
  86.             if ((act == MotionEvent.ACTION_DOWN || act == MotionEvent.ACTION_CANCEL)  
  87.                     && outBound) {  
  88.                 outBound = false;  
  89.             }  
  90.             if (!gestureDetector.onTouchEvent(ev)) {  
  91.                 outBound = false;  
  92.             } else {  
  93.                 outBound = true;  
  94.             }  
  95.             if(outBound){  
  96.                 Rect rect1 = new Rect();  
  97.                 getLocalVisibleRect(rect1);  
  98.                 TranslateAnimation am1 = new TranslateAnimation(00, rect1.top, 0);  
  99.                 am1.setDuration(300);  
  100.                 startAnimation(am1);  
  101.                 scrollTo(00);  
  102.             }  
  103.         }  
  104.         return super.dispatchTouchEvent(ev);  
  105.     };  
  106. }  

listviewPress.zip (82.6 KB)

 

十、Android SAX解析之错误纠正!!

本文转自 http://blog.youkuaiyun.com/feng88724/article/details/7013675

在讲这次错误之前,先看一下下面这段代码。  【◆以下解析方法是错误的×】

 

[java]  view plain copy print ?
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3.   
  4. import org.xml.sax.Attributes;  
  5. import org.xml.sax.SAXException;  
  6. import org.xml.sax.helpers.DefaultHandler;  
  7.   
  8. import android.util.Log;  
  9.   
  10. public class XmlHandler extends DefaultHandler{  
  11.       
  12.     private final String TAG = this.getClass().getSimpleName();  
  13.       
  14.     /**XML文件中标签定义*/  
  15.     private final String TAG_Article = "Article";  
  16.     private final String TAG_ArticleID = "ArticleID";  
  17.     private final String TAG_Title = "Title";  
  18.     private final String TAG_Date = "Date";  
  19.     private final String TAG_SmallPictures = "SmallPictures";  
  20.     private final String TAG_LargePictures = "LargePictures";  
  21.     private final String TAG_Category = "Category";  
  22.     private static final String TAG_HeadNote = "HeadNote";  
  23.     private static final String TAG_SubTitle = "SubTitle";  
  24.     private static final String TAG_Source = "Source";  
  25.       
  26.     //当前正在解析的TAG  
  27.     private String currentName;  
  28.       
  29.     //单个文章  
  30.     private News news = null;  
  31.       
  32.     //文章列表  
  33.     private List<News>  newsList = null;  
  34.       
  35.     //解析开始时间  
  36.     private long start_time;  
  37.       
  38.     private boolean flag = false;  
  39.       
  40.     @Override  
  41.     public void characters(char[] ch, int start, int length)  
  42.             throws SAXException {  
  43.         super.characters(ch, start, length);  
  44.           
  45.         if(!flag) {  
  46.             return;  
  47.         }  
  48.         // 取值  
  49.         String value = new String(ch, start, length);  
  50.         Log.d(TAG, "Element: " + currentName  + " Element Value: " + value);  
  51.         if(value != null) {  
  52.             if(TAG_ArticleID.equals(currentName)) {  
  53.                 news.setArticleId(value);  
  54.             } else if(TAG_Title.equals(currentName)) {  
  55.                 news.setTitle(value);  
  56.             } else if(TAG_Date.equals(currentName)) {  
  57.                 news.setDate(value);  
  58.             } else if(TAG_Category.equals(currentName)) {  
  59.                 news.setCategory(value);  
  60.             } else if(TAG_SmallPictures.equals(currentName)) {  
  61.                 news.setSmallPicture(value);  
  62.             } else if(TAG_LargePictures.equals(currentName)) {  
  63.                 news.setLargePicture(value);  
  64.             } else if(TAG_HeadNote.equals(currentName)) {  
  65.                 news.setHeadNote(value);  
  66.             } else if(TAG_SubTitle.equals(currentName)) {  
  67.                 news.setSubTitle(value);  
  68.             } else if(TAG_Source.equals(currentName)) {  
  69.                 news.setSource(value);  
  70.             }  
  71.         }  
  72.     }  
  73.   
  74.     @Override  
  75.     public void startDocument() throws SAXException {  
  76.         super.startDocument();  
  77.           
  78.         start_time = System.currentTimeMillis();  
  79.         newsList = new ArrayList<News>();  
  80.     }  
  81.   
  82.     @Override  
  83.     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {  
  84.         super.startElement(uri, localName, qName, attributes);  
  85.         this.currentName = localName;  
  86.         flag = true;  
  87.         if(TAG_Article.equals(localName)) {  
  88.             news = new News();  
  89.         }  
  90.     }  
  91.       
  92.     @Override  
  93.     public void endElement(String uri, String localName, String qName)  
  94.             throws SAXException {  
  95.         super.endElement(uri, localName, qName);  
  96.         flag = false;  
  97.           
  98.         if(TAG_Article.equals(localName)) {  
  99.             newsList.add(news);  
  100.         }  
  101.     }  
  102.       
  103.     @Override  
  104.     public void endDocument() throws SAXException {  
  105.         super.endDocument();  
  106.           
  107.         long end = System.currentTimeMillis();  
  108.         Log.d(TAG, "Parse List's Xml Cost: " + (end - start_time) + " !!");  
  109.     }  
  110. }  


Baidu 或者 Google 一下 “Android Sax 解析” , 给出的Sample无一例外都是如此。 坑爹啊... 甚至连有些书籍中都是这么写的, 比如《Android开发入门与实践》。(本书亲自确认过,其他书情况不详)

 

 

没错, 一般情况下,这么写是可以的, 而且在大多数情况下解析出来也是正确的。 但是就是偶尔会出错, 这个时候通常你都莫不着头脑, 怎么回事? 数据没错啊,解析部分代码貌似也没问题.. 真是奇了怪了。 其实问题都出在上面那段代码上!!

 

大家都认为 SAX 解析过程大致如下:

startDocument  ->   startElement  -> characters -> endElement -> endDocument

 

没错,就是这样, startElement  读取起始标签, endElement 读取结束标签,characters 呢?当然是读取其值, 这没错,但是大家都天真的以为 characters 只执行一次,并且一次就读取了全部内容。错就错在这!

 

其实characters 是很有可能会执行多次的,当遇到内容中有回车,\t等等内容时,它很有可能就执行多次。 有的人可能会说,那我没有这些是不是就只执行一次了? 看下我实测结果:

 

 

测试用XML如下:

 

[html]  view plain copy print ?
  1. <News>  
  2.     <Article>  
  3.         <ArticleID>1000555</ArticleID>  
  4.         <Title><![CDATA[ 郑州“亚洲第一桥”通车6年成危桥 ]]></Title>  
  5.         <Date>2011-11-25 14:23:52</Date>  
  6.         <SmallPictures>livenews/images/s20.png</SmallPictures>  
  7.         <LargePictures>livenews/images/l20.png</LargePictures>  
  8.         <Category>闻天下</Category>  
  9.         <HeadNote></HeadNote>  
  10.         <SubTitle></SubTitle>  
  11.         <Author></Author>  
  12.         <Source>人民日报</Source>  
  13.         <Abstract></Abstract>  
  14.     </Article>  
  15.     <Article>  
  16.         <ArticleID>1000554</ArticleID>  
  17.         <Title><![CDATA[ 内地事业单位拟设统一工资制度 ]]></Title>  
  18.         <Date>2011-11-25 14:22:33</Date>  
  19.         <Category><![CDATA[ 闻天下 ]]></Category>  
  20.         <HeadNote></HeadNote>  
  21.         <SubTitle></SubTitle>  
  22.         <Author></Author>  
  23.         <Source></Source>  
  24.         <Abstract></Abstract>  
  25.     </Article>  
  26.     <Article>  
  27.         <ArticleID>1000553</ArticleID>  
  28.         <Title></Title>  
  29.         <Date>2011-11-25 14:21:23</Date>  
  30.         <SmallPictures>livenews/images/s21.png</SmallPictures>  
  31.         <LargePictures>livenews/images/l21.png</LargePictures>  
  32.         <Category><![CDATA[ 星娱乐 ]]></Category>  
  33.         <HeadNote></HeadNote>  
  34.         <SubTitle></SubTitle>  
  35.         <Author></Author>  
  36.         <Source><![CDATA[ 凤凰网综合 ]]></Source>  
  37.         <Abstract></Abstract>  
  38.     </Article>  
  39. <News>  

 

可以很明显的看到,在解析 <ArticleID>1000553</ArticleID>  这一段时, characters执行了两次,将内容"1000553"分两次读取.. 用上面那种方式的最终结果就是 ArticleID = 00553 了。 那如果你的应用需要根据这个id 进一步获取内容岂不是死翘翘了?(比如这边根据id获取新闻详细内容)

 

好了,废话不多说了,看下正确的写法!  【★以下解析方法才是正确的 √ 】

 

[java]  view plain copy print ?
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3.   
  4. import org.xml.sax.Attributes;  
  5. import org.xml.sax.SAXException;  
  6. import org.xml.sax.helpers.DefaultHandler;  
  7.   
  8. import android.util.Log;  
  9.   
  10. public class XmlHandler extends DefaultHandler{  
  11.       
  12.     private final String TAG = this.getClass().getSimpleName();  
  13.       
  14.     /**XML文件中标签定义*/  
  15.     private final String TAG_Article = "Article";  
  16.     private final String TAG_ArticleID = "ArticleID";  
  17.     private final String TAG_Title = "Title";  
  18.     private final String TAG_Date = "Date";  
  19.     private final String TAG_SmallPictures = "SmallPictures";  
  20.     private final String TAG_LargePictures = "LargePictures";  
  21.     private final String TAG_Category = "Category";  
  22.     private static final String TAG_HeadNote = "HeadNote";  
  23.     private static final String TAG_SubTitle = "SubTitle";  
  24.     private static final String TAG_Source = "Source";  
  25.       
  26.     //单个文章  
  27.     private News news = null;  
  28.       
  29.     //文章列表  
  30.     private List<News>  newsList = null;  
  31.       
  32.     //解析开始时间  
  33.     private long start_time;  
  34.       
  35.     //(1)  
  36.     private StringBuilder sb = new StringBuilder();  
  37.       
  38.     @Override  
  39.     public void characters(char[] ch, int start, int length)  
  40.             throws SAXException {  
  41.         super.characters(ch, start, length);  
  42.           
  43.         //(2)不管在startElement到endElement的过程中,执行了多少次characters, 都会将内容添加到StringBuilder中,不会丢失内容  
  44.         sb.append(ch, start, length);  
  45.     }  
  46.   
  47.     @Override  
  48.     public void startDocument() throws SAXException {  
  49.         super.startDocument();  
  50.           
  51.         start_time = System.currentTimeMillis();  
  52.         newsList = new ArrayList<News>();  
  53.     }  
  54.   
  55.     @Override  
  56.     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {  
  57.         super.startElement(uri, localName, qName, attributes);  
  58.         //(3) 开始收集新的标签的数据时,先清空历史数据  
  59.         sb.setLength(0);  
  60.         if(TAG_Article.equals(localName)) {  
  61.             news = new News();  
  62.         }  
  63.     }  
  64.       
  65.     @Override  
  66.     public void endElement(String uri, String localName, String qName)  
  67.             throws SAXException {  
  68.         super.endElement(uri, localName, qName);  
  69.           
  70.         //(4)原来在characters中取值,现改在此取值  
  71.         String value = sb.toString();  
  72.           
  73.         if(TAG_ArticleID.equals(localName)) {  
  74.             news.setArticleId(value);  
  75.         } else if(TAG_Title.equals(localName)) {  
  76.             news.setTitle(value);  
  77.         } else if(TAG_Date.equals(localName)) {  
  78.             news.setDate(value);  
  79.         } else if(TAG_Category.equals(localName)) {  
  80.             news.setCategory(value);  
  81.         } else if(TAG_SmallPictures.equals(localName)) {  
  82.             news.setSmallPicture(value);  
  83.         } else if(TAG_LargePictures.equals(localName)) {  
  84.             news.setLargePicture(value);  
  85.         } else if(TAG_HeadNote.equals(localName)) {  
  86.             news.setHeadNote(value);  
  87.         } else if(TAG_SubTitle.equals(localName)) {  
  88.             news.setSubTitle(value);  
  89.         } else if(TAG_Source.equals(localName)) {  
  90.             news.setSource(value);  
  91.         }         
  92.         if(TAG_Article.equals(localName)) {  
  93.             newsList.add(news);  
  94.         }  
  95.     }  
  96.       
  97.     @Override  
  98.     public void endDocument() throws SAXException {  
  99.         super.endDocument();  
  100.           
  101.         long end = System.currentTimeMillis();  
  102.         Log.d(TAG, "Parse List's Xml Cost: " + (end - start_time) + " !!");  
  103.     }  
  104. }  


归纳为三点:

 

1.startElement的时候, new StringBuilder(); 或者 sb.setLength(0); (我建议后者)
2.characters的时候,sb.append(ch, start, length);
3.endElement的时候,sb.toString(); 此时StringBuilder中的内容才是解析的结果

 

通过这种方法就不会再有数据离奇丢失的情况了(同时也不需要像错误方法那样再设个currentTag之类的了,逻辑繁杂了,还出错)! 

 

希望大家可以尽早看到这篇文章,不要继续被吭了!!!

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值