struts2+jfreechart整合

struts2 + jfreechart整合:
1、配置环境:
 在struts2中,默认的struts-default.xml中,并没有包含chart的result-type,它是插件的形式使用的。
 把 struts2的解压包的lib里找到struts2-jfreechart-plugin-2.1.8.1.jar,拷贝到你的项目的 classpath里,
 因为这个插件里的文件struts-plugin.xml里有一个chart的result-type。内容如下:

	<package name="jfreechart-default" extends="struts-default">
       	<result-types>
    		<result-type name="chart" class="org.apache.struts2.dispatcher.ChartResult">
    			<param name="height">150</param>
    			<param name="width">200</param>
    		</result-type>
    	</result-types>
    </package>

 

 

 同时把jfreechart的库文件jfreechart-1.0.13.jar和jcommon-1.0.16.jar拷贝到你的项目的 classpath里。

 

2、修改web.xml,增加下面的struts2的配置:

	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
		<init-param>
			<param-name>struts.i18n.encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>

	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

 

 

3、在src目录下增加新建一个struts.xml配置文件,其主要内容如下:

	<!--
		关于extends继承jfreechart-default这点请大家注意
		因为在struts-default这个包里并没有result-type为chart的 chart
		定义在前面我们导入的struts2-jfreechart-plugin-2.1.8.jar 下面的struts-plugin.xml文件中
	-->
	<package name="jfreechart" extends="jfreechart-default">
		<action name="jfreechart" class="org.usc.actions.JfreeChartAction">
			<result name="success" type="chart">
				<param name="width">600</param>
				<param name="height">400</param>
			</result>
		</action>
	</package>

 

 

4、写测试页面:

	<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
	<%
	String path = request.getContextPath();
	String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
	%>
	<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
	<html>
	  <head>
		<base href="<%=basePath%>">
		<title>jfreechart</title>
	  </head>
	  <body>
		 <img alt="jfreechart" src="jfreechart.action"/>
	  </body>
	</html>

 

 

5、在你的action代码里,返回一个JFreeChart对象即可:

	/**
	 * File   : JfreeChartAction.java
	 * Author : zqding
	 * Date   : 2010-7-27
	 * Version:
	 * Desc   : 	
	 */
	package org.usc.actions;

	import org.jfree.chart.ChartFactory;
	import org.jfree.chart.JFreeChart;
	import org.jfree.data.general.DefaultPieDataset;

	import com.opensymphony.xwork2.ActionSupport;

	public class JfreeChartAction extends ActionSupport {
		/**
		 * 定义JFreeChart对象 大家请注意在这里JFreeChart对象名只能为chart 
		 * 不能是别的, 关于这点大家可以上struts2网站上面查看一下
		 * http://struts.apache.org/2.x/docs/jfreechart-plugin.html
		 */
		private JFreeChart chart;
		
		public JFreeChart getChart() {
			return chart;
		}

		public void setChart(JFreeChart chart) {
			this.chart = chart;
		}

		@Override
		public String execute() throws Exception {
			 //设置数据
			DefaultPieDataset data = new DefaultPieDataset();
			data.setValue("Java", new Double(43.2));
			data.setValue("Visual Basic", new Double(1.0));
			data.setValue("C/C++", new Double(17.5)); 
			data.setValue("Android", new Double(60.0));
			//设置字体及生成的图片格式设置略,请参照API文档
			//生成JFreeChart对象
			chart = ChartFactory.createPieChart("Programme Language", data, true,true, false);
			return SUCCESS;
		}
	}

 

 6、发布到应用服务器,进行测试。
 http://localhost:9090/SSHDemo/jfreechart.jsp
 如下图:

jfreechart

android客户端向服务器提交请求的中文乱码问题

     下午尝试着从android客户端提交中文字符到服务器端,服务器是tomcat的,利用android自带的apache的httpclient组件,用post方式提交。

     客户端提交请求的代码如下:

     DefaultHttpClient mHttpClient = new DefaultHttpClient();
     HttpPost mPost = new HttpPost("http://10.0.2.2:808/AndroidServer/userService.service");
     List<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>(); 
     pairs.add(new BasicNameValuePair("method","addUser"));
     pairs.add(new BasicNameValuePair("email","king@163.com"));
     pairs.add(new BasicNameValuePair("password","112358"));
     pairs.add(new BasicNameValuePair("site.province","北京市"));
     pairs.add(new BasicNameValuePair("site.city","东城区"));
     try {
	mPost.setEntity(new UrlEncodedFormEntity(pairs,HTTP.UTF_8));
	HttpResponse response = mHttpClient.execute(mPost);
      } catch (UnsupportedEncodingException e) {
	// TODO Auto-generated catch block
	 e.printStackTrace();
      } catch (ClientProtocolException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
      } catch (IOException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
      } catch (IllegalStateException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
      } 

    在服务器端提取中文数据后仍然是乱码,但奇怪的是,如果在PC客户端用main方法提交,完全相同的代码,服务器端却能正确提取中文字符,难道是android本身的问题?

    期待高手能解答,下面说下解决方案吧,其实很简单,我在客户端把中文字符用URLEncoder.encode()进行转码,在服务器端用URLDecoder.decode()进行解码,这样就能正常的提取中文字符了,改动过的客户端代码如下:

  pairs.add(new BasicNameValuePair("site.province",URLEncoder.encode("北京市")));
  pairs.add(new BasicNameValuePair("site.city",URLEncoder.encode("东城区")));

游戏开发4_01数据存储–io

package wyf.wpf;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import org.apache.http.util.EncodingUtils;

import android.app.Activity;

import android.os.Bundle;

import android.widget.TextView;

public class Sample_4_1 extends Activity {

public static final String ENCODING = “UTF-8”; //常量,代表编码格式

String fileName = “test.txt”;//文件的名称

String message = “你好,这是一个关于文件I/O的示例。”; //写入和读出的数据信息

TextView tv; //TextView对象引用

    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);//设置当前屏幕

writeFileData(fileName, message);//创建文件并写入数据

        String result = readFileData(fileName);//获得从文件读入的数据

        tv = (TextView)findViewById(R.id.tv);//根据id获取屏幕中TextView对象的引用

        tv.setText(result);//设置TextView的内容

    }

    //方法:向指定文件中写入指定的数据

    public void writeFileData(String fileName,String message){

    try{

    FileOutputStream fout = openFileOutput(fileName, MODE_PRIVATE);//获得FileOutputStream对象

    byte [] bytes = message.getBytes();//将要写入的字符串转换为byte数组

    fout.write(bytes);//将byte数组写入文件

    fout.close();//关闭FileOutputStream对象

    }

    catch(Exception e){

    e.printStackTrace();//捕获异常并打印

    }

    }   

    //方法:打开指定文件,读取其数据,返回字符串对象

    public String readFileData(String fileName){

    String result=””;

    try{

    FileInputStream fin = openFileInput(fileName);//获得FileInputStream对象

    int length = fin.available();//获取文件长度

    byte [] buffer = new byte[length];//创建byte数组用于读入数据

    fin.read(buffer);//将文件内容读入到byte数组中   

    result = EncodingUtils.getString(buffer, ENCODING);//将byte数组转换成指定格式的字符串

    fin.close(); //关闭文件输入流

    }

    catch(Exception e){

    e.printStackTrace();//捕获异常并打印

    }

    return result;//返回读到的数据字符串

    }   

}

Android中使用HttpClient访问https时,安全证书的处理

     Android中包含了Apache Jakarta Common 下的子项目 HttpClient 类包的一个子集。因此,在大多数情况下可以按照JVM的方式使用HttpClient,但是在有些情况下,由于Android SDK中未包含某些类,处理的方式就有差别。比如,访问https时安全证书的处理。

    假如我们的需求是自动接受所有安全证书。思路跟以前一样,扩展一个SSLSocketFactory类,并将TrustManager里的方法全部重写成空。

    为了方便使用,我直接在Android的org.apache.http.conn.ssl.SSLSocketFactory源码上扩展。

    主要修改了其默认构造方法,如下:

 

 

     private CustomSSLSocketFactory() throws NoSuchAlgorithmException, KeyManagementException {
        super();
//        this.sslcontext = null;
//        this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory();
//        this.nameResolver = null;
        this.sslcontext = SSLContext.getInstance(TLS);
        this.sslcontext.init(null, new TrustManager[]{
                new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                    public void checkClientTrusted(
                        java.security.cert.X509Certificate[] certs, String authType) {
                    }
                    public void checkServerTrusted(
                        java.security.cert.X509Certificate[] certs, String authType) {
                    }
                }
            }, new SecureRandom());
        this.socketfactory = this.sslcontext.getSocketFactory();
        this.nameResolver = null;
    }

 

定义好SSLSocketFactory之后,再使用它来配置HttpClient并访问Https就和以前一样了。

 

 

转载请注明

转:draggable可拖动的ListView,并且支持行删除功能

看图,拖动前: 
 
拖动后: 
 

 

package com.ql.view;  
  
import com.ql.activity.R;  
  
import android.content.Context;  
import android.graphics.Bitmap;  
import android.graphics.PixelFormat;  
import android.graphics.Rect;  
import android.util.AttributeSet;  
import android.util.Log;  
import android.view.Gravity;  
import android.view.MotionEvent;  
import android.view.View;  
import android.view.ViewConfiguration;  
import android.view.ViewGroup;  
import android.view.WindowManager;  
import android.widget.AdapterView;  
import android.widget.ImageView;  
import android.widget.ListView;  
  
/** 
 * draggable可拖动的列表,并且支持行删除功能 
 * @author admin 
 * 
 */  
public class DraggableListView extends ListView {  
  
    private DropListener mDropListener;  
  
    private ImageView mDragView;  
    private int mDragPos; // which item is being dragged  
    private int mFirstDragPos; // where was the dragged item originally  
    private int mDragPoint; // at what offset inside the item did the user grab  
                            // it  
    private int mCoordOffset; // the difference between screen coordinates and  
                                // coordinates in this view  
  
    private Rect mTempRect = new Rect();  
    private final int mTouchSlop;  
    private int mHeight;  
    private int mUpperBound;  
    private int mLowerBound;  
    private WindowManager mWindowManager;  
    private WindowManager.LayoutParams mWindowParams;  
    private int dragndropBackgroundColor = 0x00000000;  
    private Bitmap mDragBitmap;  
    private int mItemHeightHalf = 32;  
    private int mItemHeightNormal = 64;  
    private int mItemHeightExpanded = 128;  
    //private int grabberId=-1;  
  
    public DraggableListView(Context context, AttributeSet attrs) {  
        this(context, attrs, 0);  
        // TODO Auto-generated constructor stub  
    }  
  
    public DraggableListView(Context context, AttributeSet attrs, int defStyle) {  
        super(context, attrs, defStyle);  
        // TODO Auto-generated constructor stub  
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  
          
//      if (attrs!=null) {  
//          TypedArray a=getContext().obtainStyledAttributes(attrs,R.styleable.TouchListView,0, 0);  
//  
//          mItemHeightNormal=a.getDimensionPixelSize(R.styleable.TouchListView_normal_height, 0);  
//          mItemHeightExpanded=a.getDimensionPixelSize(R.styleable.TouchListView_expanded_height, mItemHeightNormal);  
//          grabberId=a.getResourceId(R.styleable.TouchListView_grabber, -1);  
//          dragndropBackgroundColor=a.getColor(R.styleable.TouchListView_dragndrop_background, 0x00000000);  
//          //mRemoveMode=a.getInt(R.styleable.TouchListView_remove_mode, -1);  
//  
//          a.recycle();  
//      }  
  
    }  
  
    @Override  
    public boolean onTouchEvent(MotionEvent ev) {  
        // TODO Auto-generated method stub  
//      Log.v(">>>>>>>>>>onTouchEvent", ">>>>>>>>>>onTouchEvent");  
        if ((mDropListener != null) && mDragView != null) {  
            int action = ev.getAction();  
            switch (action) {  
            case MotionEvent.ACTION_UP:  
            case MotionEvent.ACTION_CANCEL:  
                Rect r = mTempRect;  
                mDragView.getDrawingRect(r);  
                stopDragging();  
                if (mDropListener != null && mDragPos >= 0  
                        && mDragPos < getCount()) {  
                    mDropListener.drop(mFirstDragPos, mDragPos);  
                }  
                unExpandViews(false);  
                break;  
  
            case MotionEvent.ACTION_DOWN:  
            case MotionEvent.ACTION_MOVE:  
                int x = (int) ev.getX();  
                int y = (int) ev.getY();  
                dragView(x, y);  
  
                int itemnum = getItemForPosition(y);  
                if (itemnum >= 0) {  
                    if (action == MotionEvent.ACTION_DOWN  
                            || itemnum != mDragPos) {  
  
                        mDragPos = itemnum;  
                        doExpansion();  
//                      Log.v(">>>doExpansion", ">>>>>>>>>>doExpansion");  
                    }  
                    /* 
                    int speed = 0; 
                    adjustScrollBounds(y); 
                    if (y > mLowerBound) { 
                        // scroll the list up a bit 
                        speed = y > (mHeight + mLowerBound) / 2   16 : 4; 
                    } else if (y < mUpperBound) { 
                        // scroll the list down a bit 
                        speed = y < mUpperBound / 2   -16 : -4; 
                    } 
                    if (speed != 0) { 
                        int ref = pointToPosition(0, mHeight / 2); 
                        if (ref == AdapterView.INVALID_POSITION) { 
                            // we hit a divider or an invisible view, check 
                            // somewhere else 
                            ref = pointToPosition(0, mHeight / 2 
                                    + getDividerHeight() + 64); 
                        } 
                        View v = getChildAt(ref - getFirstVisiblePosition()); 
                        if (v != null) { 
                            int pos = v.getTop(); 
                            setSelectionFromTop(ref, pos - speed); 
 
                        } 
                    } 
                    */  
                }  
                break;  
            }  
            return true;  
        }  
        return super.onTouchEvent(ev);  
  
    }  
  
    @Override  
    public boolean onInterceptTouchEvent(MotionEvent ev) {  
        // TODO Auto-generated method stub  
        if (mDropListener != null) {  
            switch (ev.getAction()) {  
            case MotionEvent.ACTION_DOWN:  
                int x = (int) ev.getX();  
                int y = (int) ev.getY();  
                int itemnum = pointToPosition(x, y);  
//              Log.v("itemnum>>>", ">>>>>>>>" + itemnum);  
                if (itemnum == AdapterView.INVALID_POSITION) {  
                    break;  
                }  
                ViewGroup item = (ViewGroup) getChildAt(itemnum  
                        - getFirstVisiblePosition());  
//              Log.v("itemnum>>>", ">>>>>>>>" + getFirstVisiblePosition()  
//                      + "---" + ev.getRawY() + "----" + ev.getY()+"-----"+item.getTop());  
                mDragPoint = y - item.getTop();  
                mCoordOffset = ((int) ev.getRawY()) - y;  
                View dragger = item.findViewById(R.id.iconimg);//拖动的ImageView  
                Rect r = mTempRect;  
                // dragger.getDrawingRect(r);  
  
                r.left = dragger.getLeft();  
                r.right = dragger.getRight();  
                r.top = dragger.getTop();  
                r.bottom = dragger.getBottom();  
  
                if ((r.left < x) && (x < r.right)) {  
                    item.setDrawingCacheEnabled(true);  
                    // Create a copy of the drawing cache so that it does not  
                    // get recycled  
                    // by the framework when the list tries to clean up memory  
                    Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());  
                    startDragging(bitmap, y);  
                    mDragPos = itemnum;  
                    mFirstDragPos = mDragPos;  
                    mHeight = getHeight();  
                    int touchSlop = mTouchSlop;  
                    mUpperBound = Math.min(y - touchSlop, mHeight / 3);  
                    mLowerBound = Math.max(y + touchSlop, mHeight * 2 / 3);  
                    return false;  
                }  
                //  
                View delView = item.findViewById(R.id.delete);//删除的ImageView  
                r.left = delView.getLeft();  
                r.right = delView.getRight();  
                r.top = delView.getTop();  
                r.bottom = delView.getBottom();  
                if ((r.left < x) && (x < r.right)) {  
                    mDropListener.onDeleteClicked(itemnum);  
                    return false;  
                }  
  
                mDragView = null;  
                break;  
            }  
        }  
        return super.onInterceptTouchEvent(ev);  
    }  
  
    private void startDragging(Bitmap bm, int y) {  
        stopDragging();  
  
        mWindowParams = new WindowManager.LayoutParams();  
        mWindowParams.gravity = Gravity.TOP;  
        mWindowParams.x = 0;  
        mWindowParams.y = y - mDragPoint + mCoordOffset;  
  
        mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;  
        mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;  
        mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE  
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON  
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;  
        mWindowParams.format = PixelFormat.TRANSLUCENT;  
        mWindowParams.windowAnimations = 0;  
  
        ImageView v = new ImageView(getContext());  
        // int backGroundColor =  
        // getContext().getResources().getColor(R.color.dragndrop_background);  
        v.setBackgroundColor(dragndropBackgroundColor);  
        v.setImageBitmap(bm);  
        mDragBitmap = bm;  
  
        mWindowManager = (WindowManager) getContext()  
                .getSystemService("window");  
        mWindowManager.addView(v, mWindowParams);  
        mDragView = v;  
    }  
  
    private void stopDragging() {  
        if (mDragView != null) {  
            WindowManager wm = (WindowManager) getContext().getSystemService(  
                    "window");  
            wm.removeView(mDragView);  
            mDragView.setImageDrawable(null);  
            mDragView = null;  
        }  
        if (mDragBitmap != null) {  
            mDragBitmap.recycle();  
            mDragBitmap = null;  
        }  
    }  
  
    private void dragView(int x, int y) {  
        float alpha = 1.0f;  
        mWindowParams.alpha = alpha;  
        // }  
        mWindowParams.y = y - mDragPoint + mCoordOffset;  
        mWindowManager.updateViewLayout(mDragView, mWindowParams);  
    }  
  
    private int getItemForPosition(int y) {  
        int adjustedy = y - mDragPoint - mItemHeightHalf;  
        int pos = myPointToPosition(0, adjustedy);  
        if (pos >= 0) {  
            if (pos <= mFirstDragPos) {  
                pos += 1;  
            }  
        } else if (adjustedy < 0) {  
            pos = 0;  
        }  
        return pos;  
    }  
  
    private void adjustScrollBounds(int y) {  
        if (y >= mHeight / 3) {  
            mUpperBound = mHeight / 3;  
        }  
        if (y <= mHeight * 2 / 3) {  
            mLowerBound = mHeight * 2 / 3;  
        }  
    }  
  
    /* 
     * Restore size and visibility for all listitems 
     */  
    private void unExpandViews(boolean deletion) {  
        for (int i = 0;; i++) {  
            View v = getChildAt(i);  
            if (v == null) {  
                if (deletion) {  
                    // HACK force update of mItemCount  
                    int position = getFirstVisiblePosition();  
                    int y = getChildAt(0).getTop();  
                    setAdapter(getAdapter());  
                    setSelectionFromTop(position, y);  
                    // end hack  
                }  
                layoutChildren(); // force children to be recreated where needed  
                v = getChildAt(i);  
                if (v == null) {  
                    break;  
                }  
            }  
            ViewGroup.LayoutParams params = v.getLayoutParams();  
            params.height = mItemHeightNormal;  
            v.setLayoutParams(params);  
            v.setVisibility(View.VISIBLE);  
        }  
    }  
  
    /* 
     * Adjust visibility and size to make it appear as though an item is being 
     * dragged around and other items are making room for it: If dropping the 
     * item would result in it still being in the same place, then make the 
     * dragged listitem's size normal, but make the item invisible. Otherwise, 
     * if the dragged listitem is still on screen, make it as small as possible 
     * and expand the item below the insert point. If the dragged item is not on 
     * screen, only expand the item below the current insertpoint. 
     */  
    private void doExpansion() {  
        int childnum = mDragPos - getFirstVisiblePosition();  
        if (mDragPos > mFirstDragPos) {  
            childnum++;  
        }  
  
        View first = getChildAt(mFirstDragPos - getFirstVisiblePosition());  
  
        for (int i = 0;; i++) {  
            View vv = getChildAt(i);  
            if (vv == null) {  
                break;  
            }  
            int height = mItemHeightNormal;  
            int visibility = View.VISIBLE;  
            if (vv.equals(first)) {  
                // processing the item that is being dragged  
                if (mDragPos == mFirstDragPos) {  
                    // hovering over the original location  
                    visibility = View.INVISIBLE;  
                } else {  
                    // not hovering over it  
                    height = 1;  
                }  
            } else if (i == childnum) {  
                if (mDragPos < getCount() - 1) {  
                    height = mItemHeightExpanded;  
                }  
            }  
            ViewGroup.LayoutParams params = vv.getLayoutParams();  
            params.height = height;  
            vv.setLayoutParams(params);  
            vv.setVisibility(visibility);  
        }  
    }  
  
    /* 
     * pointToPosition() doesn't consider invisible views, but we need to, so 
     * implement a slightly different version. 
     */  
    private int myPointToPosition(int x, int y) {  
        Rect frame = mTempRect;  
        final int count = getChildCount();  
        for (int i = count - 1; i >= 0; i--) {  
            final View child = getChildAt(i);  
            child.getHitRect(frame);  
            if (frame.contains(x, y)) {  
                return getFirstVisiblePosition() + i;  
            }  
        }  
        return INVALID_POSITION;  
    }  
  
    public interface DropListener {  
        //  
        void drop(int from, int to);  
        //  
        void onDeleteClicked(int index);  
    }  
  
    public void setDropListener(DropListener onDrop) {  
        // TODO Auto-generated method stub  
        mDropListener = onDrop;  
    }  
  
}  

 package com.ql.activity;

 

  
import java.util.ArrayList;  
import java.util.HashMap;  
import java.util.Map;  
  
  
import android.app.Activity;  
import android.app.AlertDialog;  
import android.content.DialogInterface;  
import android.os.Bundle;  
import android.view.LayoutInflater;  
import android.view.View;  
import android.view.ViewGroup;  
import android.widget.ArrayAdapter;  
import android.widget.TextView;  
  
import com.ql.view.DraggableListView;  
  
  
public class Test_6_Activity  extends Activity{  
    private DraggableListView   mListView;  
    private DraggableArrayAdapter adapter = null;  
    private ArrayList<Map<String, String>> array;  
//  private int mIndex;  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.test5);  
          
        mListView = (DraggableListView) findViewById(R.id.draggable_list);    
        array = getData();  
        adapter = new DraggableArrayAdapter();  
        mListView.setAdapter(adapter);  
          
        mListView.setDropListener(onDrop);  
//      mListView.getAdapter();  
//      //列表点击事件处理  
//        mListView.setOnItemClickListener(new OnItemClickListener() {  
//  
//          @Override  
//          public void onItemClick(AdapterView< > arg0, View arg1, int arg2,  
//                                  long arg3) {  
//                
//              //Toast.makeText(context, "Click "+arg2, Toast.LENGTH_SHORT).show();  
//          }  
//        });     
//          
//        mListView.setOnItemLongClickListener(new OnItemLongClickListener() {  
//            
//          @Override  
//          public boolean onItemLongClick(AdapterView< > arg0, View arg1, int position,  
//                  long id) {  
//              mIndex = position;  
//              return false;  
//          }  
//        });  
      
    }  
    //初始化数据  
    private ArrayList<Map<String, String>> getData() {  
        ArrayList<Map<String, String>> list = new ArrayList<Map<String, String>>();  
        for(int i=0; i<5; i++) {  
            Map<String, String> map = new HashMap<String, String>();  
            map.put("code", "code"+i);  
            map.put("name", "name"+i);  
            list.add(map);  
        }  
        return list;  
    }  
  
    private DraggableListView.DropListener onDrop = new DraggableListView.DropListener() {  
        @Override  
        public void drop(int from, int to) {  
            Map<String, String> item = adapter.getItem(from);  
            adapter.remove(item);  
            adapter.insert(item, to);  
        }  
  
        @Override  
        public void onDeleteClicked(int index) {  
//          mIndex = index;  
            delete(index);  
        }  
    };  
    private void delete(final int index) {  
        String prompt = "delete "+array.get(index).get("name").toString()+" "+array.get(index).get("code").toString()+" ";  
        //删除确认对话          
        new AlertDialog.Builder(Test_6_Activity.this)  
            .setTitle("删除 ")  
            //.setIcon(android.R.drawable.ic_menu_help)  
            .setMessage(prompt)  
            .setCancelable(true)  
            .setPositiveButton("确定", new DialogInterface.OnClickListener() {  
                  
                @Override  
                public void onClick(DialogInterface dialog, int which) {  
                    Map<String, String> item = adapter.getItem(index);  
                    adapter.remove(item);  
                }  
            })  
            .setNegativeButton("取消", new DialogInterface.OnClickListener() {  
                  
                @Override  
                public void onClick(DialogInterface dialog, int which) {  
                    dialog.cancel();  
                }  
            }).show();  
  
    }  
    /** 
     * 适配器 
     * @author admin 
     * 
     */  
    class DraggableArrayAdapter extends ArrayAdapter<Map<String, String>> {  
  
        DraggableArrayAdapter() {  
              
            super(Test_6_Activity.this, R.layout.row_simple_list_item_4, array);  
        }  
  
        public ArrayList<Map<String, String>> getList() {  
            return array;  
        }  
  
        public View getView(int position, View convertView, ViewGroup parent) {  
              
            View row = convertView;  
            if (row == null) {  
                LayoutInflater inflater = getLayoutInflater();  
                row = inflater.inflate(R.layout.row_simple_list_item_4, parent, false);  
            }  
            TextView code = (TextView) row.findViewById(R.id.code);  
            code.setText(array.get(position).get("code").toString());  
            TextView name = (TextView) row.findViewById(R.id.name);  
            name.setText(array.get(position).get("name").toString());  
  
            return (row);  
        }  
    }  
}  

 < xml version=”1.0″ encoding=”utf-8″ >

<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent">  
  
    <com.ql.view.DraggableListView  
        android:id="@+id/draggable_list"  
        android:layout_width="fill_parent"   
        android:layout_height="fill_parent"  
        />  
</LinearLayout>  

 < xml version=”1.0″ encoding=”utf-8″ >

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="fill_parent"  
    android:layout_height="wrap_content"  
    >  
    <!--  
    android:background="@drawable/list_bg" 
     -->  
    <ImageView android:id="@+id/iconimg"  
        android:layout_alignParentLeft="true"  
        android:layout_alignParentTop="true"  
        android:layout_alignParentBottom="true"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:src="@drawable/grabber"  
        />  
          
    <ImageView android:id="@+id/delete"  
        android:layout_alignParentRight="true"  
        android:layout_centerVertical="true"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:paddingRight="5dip"  
        android:src="@android:drawable/ic_delete"  
        />  
          
    <TextView android:id="@+id/code"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:paddingLeft="10dip"  
        android:layout_toRightOf="@id/iconimg"  
        android:layout_centerVertical="true"  
        android:textSize="20dip"  
        android:textColor="#888888"  
        />  
    <TextView android:id="@+id/name"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:paddingLeft="10dip"  
        android:layout_toRightOf="@id/code"  
        android:layout_centerVertical="true"  
        android:textSize="20dip"  
        android:textColor="#888888"  
        />  
          
</RelativeLayout>  

 另一个ListView: 
http://blog.csdn.net/sodino/archive/2010/12/15/6077017.aspx 
1.实现根据字母进行分类。 
2.实现快速滑动。 
3.实现快速滑动的提示。 
4.实现快捷弹窗。 

自定义Android ListView控件:ExpandableListView 
http://www.pin5i.com/showtopic-custom-android-listview-control-expandablelistview.html 

另一篇关于listview的拖动效果 
android listview拖拽,拖动item 改变位置 
http://blog.csdn.net/dany1202/archive/2010/12/31/6109160.aspx 
在packages/apps/Music/src/touchIncepter.java中 
该类提供了listview的拖动效果,并提供接口,在程序接口中实现数据的交换即可。 

 

package com.and.DragListview;    
import java.util.ArrayList;    
import java.util.List;    
import android.app.ListActivity;    
import android.content.Context;    
import android.os.Bundle;    
import android.view.LayoutInflater;    
import android.view.View;    
import android.view.ViewGroup;    
import android.widget.BaseAdapter;    
import android.widget.ImageView;    
import android.widget.TextView;    
public class DragListview extends ListActivity {       
    MyAdapter adapter;    
    TouchInterceptor list;    
    List<String> arrayText;    
        
    @Override    
    public void onCreate(Bundle savedInstanceState) {    
        super.onCreate(savedInstanceState);    
        setContentView(R.layout.main);    
            
        list = (TouchInterceptor) getListView();//(TouchInterceptor)findViewById(android.R.id.list);    
        getText();    
            
        adapter = new MyAdapter(this);    
        setListAdapter(adapter);    
           
        list.setDropListener(mDropListener);    
     //   list.setRemoveListener(mRemoveListener);          
    }    
    public void getText(){    
        arrayText = new ArrayList<String>();    
        arrayText.add("传奇");    
        arrayText.add("红豆");    
        arrayText.add("流年");    
        arrayText.add("棋子");    
    }    
        
    //交换listview的数据    
    private TouchInterceptor.DropListener mDropListener =    
        new TouchInterceptor.DropListener() {    
        public void drop(int from, int to) {    
            String item = arrayText.get(from);    
            arrayText.remove(item);//.remove(from);    
            arrayText.add(to, item);    
            adapter.notifyDataSetChanged();    
        }    
    };    
        
    private TouchInterceptor.RemoveListener mRemoveListener =    
        new TouchInterceptor.RemoveListener() {    
        public void remove(int which) {              
        }    
    };    
        
    class MyAdapter extends BaseAdapter{    
        private LayoutInflater mInflater;    
        Context mContext;    
        public MyAdapter(Context c){    
            mInflater = LayoutInflater.from(c);    
        }    
        public int getCount() {             
            return arrayText.size();    
        }    
        public Object getItem(int arg0) {    
            return arrayText.get(arg0);    
        }    
        public long getItemId(int arg0) {    
            return arg0;    
        }    
        public View getView(int arg0, View contentView, ViewGroup arg2) {    
            ImageView img;    
            TextView text;    
            if(contentView==null){    
                contentView = mInflater.inflate(R.layout.list_layout, null);     
                //contentView = mInflater.inflate(R.layout.list_layout,null);    
            }    
            img = (ImageView)contentView.findViewById(R.id.img);    
            img.setBackgroundResource(R.drawable.icon);    
            text = (TextView)contentView.findViewById(R.id.text);    
            text.setText(arrayText.get(arg0).toString());    
                
            return contentView;    
        }    
            
    }    
}    

 /*

 * Copyright (C) 2008 The Android Open Source Project  
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");  
 * you may not use this file except in compliance with the License.  
 * You may obtain a copy of the License at  
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0  
 *  
 * Unless required by applicable law or agreed to in writing, software  
 * distributed under the License is distributed on an "AS IS" BASIS,  
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
 * See the License for the specific language governing permissions and  
 * limitations under the License.  
 */    
package com.and.DragListview;    
import android.content.Context;    
import android.content.SharedPreferences;    
import android.content.res.Resources;    
import android.graphics.Bitmap;    
import android.graphics.PixelFormat;    
import android.graphics.Rect;    
import android.util.AttributeSet;    
import android.view.GestureDetector;    
import android.view.Gravity;    
import android.view.MotionEvent;    
import android.view.View;    
import android.view.ViewConfiguration;    
import android.view.ViewGroup;    
import android.view.WindowManager;    
import android.view.GestureDetector.SimpleOnGestureListener;    
import android.widget.AdapterView;    
import android.widget.ImageView;    
import android.widget.ListView;    
public class TouchInterceptor extends ListView {    
        
    private ImageView mDragView;    
    private WindowManager mWindowManager;    
    private WindowManager.LayoutParams mWindowParams;    
    private int mDragPos;      // which item is being dragged    
    private int mFirstDragPos; // where was the dragged item originally    
    private int mDragPoint;    // at what offset inside the item did the user grab it    
    private int mCoordOffset;  // the difference between screen coordinates and coordinates in this view    
    private DragListener mDragListener;    
    private DropListener mDropListener;    
    private RemoveListener mRemoveListener;    
    private int mUpperBound;    
    private int mLowerBound;    
    private int mHeight;    
    private GestureDetector mGestureDetector;    
    private static final int FLING = 0;    
    private static final int SLIDE = 1;    
    private int mRemoveMode = -1;    
    private Rect mTempRect = new Rect();    
    private Bitmap mDragBitmap;    
    private final int mTouchSlop;    
    private int mItemHeightNormal;    
    private int mItemHeightExpanded;    
    private int mItemHeightHalf;    
    public TouchInterceptor(Context context, AttributeSet attrs) {    
        super(context, attrs);    
        SharedPreferences pref = context.getSharedPreferences("Music", 3);    
        mRemoveMode = pref.getInt("deletemode", -1);    
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();    
        Resources res = getResources();    
        mItemHeightNormal = 48;//res.getDimensionPixelSize(R.dimen.normal_height);    
        mItemHeightHalf = mItemHeightNormal / 2;    
        mItemHeightExpanded = 48;//res.getDimensionPixelSize(R.dimen.expanded_height);    
    }    
        
    @Override    
    public boolean onInterceptTouchEvent(MotionEvent ev) {    
        if (mRemoveListener != null && mGestureDetector == null) {    
            if (mRemoveMode == FLING) {    
                mGestureDetector = new GestureDetector(getContext(), new SimpleOnGestureListener() {    
                    @Override    
                    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,    
                            float velocityY) {    
                        if (mDragView != null) {    
                            if (velocityX > 1000) {    
                                Rect r = mTempRect;    
                                mDragView.getDrawingRect(r);    
                                if ( e2.getX() > r.right * 2 / 3) {    
                                    // fast fling right with release near the right edge of the screen    
                                    stopDragging();    
                                    mRemoveListener.remove(mFirstDragPos);    
                                    unExpandViews(true);    
                                }    
                            }    
                            // flinging while dragging should have no effect    
                            return true;    
                        }    
                        return false;    
                    }    
                });    
            }    
        }    
        if (mDragListener != null || mDropListener != null) {    
            switch (ev.getAction()) {    
                case MotionEvent.ACTION_DOWN:    
                    int x = (int) ev.getX();    
                    int y = (int) ev.getY();    
                    int itemnum = pointToPosition(x, y);    
                    if (itemnum == AdapterView.INVALID_POSITION) {    
                        break;    
                    }    
                    ViewGroup item = (ViewGroup) getChildAt(itemnum - getFirstVisiblePosition());    
                    mDragPoint = y - item.getTop();    
                    mCoordOffset = ((int)ev.getRawY()) - y;    
                    View dragger = item.findViewById(R.id.img);//..........................    
                    Rect r = mTempRect;    
                    dragger.getDrawingRect(r);    
                    // The dragger icon itself is quite small, so pretend the touch area is bigger    
                    if (x < r.right * 2) {    
                        item.setDrawingCacheEnabled(true);    
                        // Create a copy of the drawing cache so that it does not get recycled    
                        // by the framework when the list tries to clean up memory    
                        Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());    
                        startDragging(bitmap, y);    
                        mDragPos = itemnum;    
                        mFirstDragPos = mDragPos;    
                        mHeight = getHeight();    
                        int touchSlop = mTouchSlop;    
                        mUpperBound = Math.min(y - touchSlop, mHeight / 3);    
                        mLowerBound = Math.max(y + touchSlop, mHeight * 2 /3);    
                        return false;    
                    }    
                    stopDragging();    
                    break;    
            }    
        }    
        return super.onInterceptTouchEvent(ev);    
    }    
        
    /*  
     * pointToPosition() doesn't consider invisible views, but we  
     * need to, so implement a slightly different version.  
     */    
    private int myPointToPosition(int x, int y) {    
        if (y < 0) {    
            // when dragging off the top of the screen, calculate position    
            // by going back from a visible item    
            int pos = myPointToPosition(x, y + mItemHeightNormal);    
            if (pos > 0) {    
                return pos - 1;    
            }    
        }    
        Rect frame = mTempRect;    
        final int count = getChildCount();    
        for (int i = count - 1; i >= 0; i--) {    
            final View child = getChildAt(i);    
            child.getHitRect(frame);    
            if (frame.contains(x, y)) {    
                return getFirstVisiblePosition() + i;    
            }    
        }    
        return INVALID_POSITION;    
    }    
        
    private int getItemForPosition(int y) {    
        int adjustedy = y - mDragPoint - mItemHeightHalf;    
        int pos = myPointToPosition(0, adjustedy);    
        if (pos >= 0) {    
            if (pos <= mFirstDragPos) {    
                pos += 1;    
            }    
        } else if (adjustedy < 0) {    
            // this shouldn't happen anymore now that myPointToPosition deals    
            // with this situation    
            pos = 0;    
        }    
        return pos;    
    }    
        
    private void adjustScrollBounds(int y) {    
        if (y >= mHeight / 3) {    
            mUpperBound = mHeight / 3;    
        }    
        if (y <= mHeight * 2 / 3) {    
            mLowerBound = mHeight * 2 / 3;    
        }    
    }    
    /*  
     * Restore size and visibility for all listitems  
     */    
    private void unExpandViews(boolean deletion) {    
        for (int i = 0;; i++) {    
            View v = getChildAt(i);    
            if (v == null) {    
                if (deletion) {    
                    // HACK force update of mItemCount    
                    int position = getFirstVisiblePosition();    
                    int y = getChildAt(0).getTop();    
                    setAdapter(getAdapter());    
                    setSelectionFromTop(position, y);    
                    // end hack    
                }    
                layoutChildren(); // force children to be recreated where needed    
                v = getChildAt(i);    
                if (v == null) {    
                    break;    
                }    
            }    
            ViewGroup.LayoutParams params = v.getLayoutParams();    
            params.height = mItemHeightNormal;    
            v.setLayoutParams(params);    
            v.setVisibility(View.VISIBLE);    
        }    
    }    
        
    /* Adjust visibility and size to make it appear as though  
     * an item is being dragged around and other items are making  
     * room for it:  
     * If dropping the item would result in it still being in the  
     * same place, then make the dragged listitem's size normal,  
     * but make the item invisible.  
     * Otherwise, if the dragged listitem is still on screen, make  
     * it as small as possible and expand the item below the insert  
     * point.  
     * If the dragged item is not on screen, only expand the item  
     * below the current insertpoint.  
     */    
    private void doExpansion() {    
        int childnum = mDragPos - getFirstVisiblePosition();    
        if (mDragPos > mFirstDragPos) {    
            childnum++;    
        }    
        View first = getChildAt(mFirstDragPos - getFirstVisiblePosition());    
        for (int i = 0;; i++) {    
            View vv = getChildAt(i);    
            if (vv == null) {    
                break;    
            }    
            int height = mItemHeightNormal;    
            int visibility = View.VISIBLE;    
            if (vv.equals(first)) {    
                // processing the item that is being dragged    
                if (mDragPos == mFirstDragPos) {    
                    // hovering over the original location    
                    visibility = View.INVISIBLE;    
                } else {    
                    // not hovering over it    
                    height = 1;    
                }    
            } else if (i == childnum) {    
                if (mDragPos < getCount() - 1) {    
                    height = mItemHeightExpanded;    
                }    
            }    
            ViewGroup.LayoutParams params = vv.getLayoutParams();    
            params.height = height;    
            vv.setLayoutParams(params);    
            vv.setVisibility(visibility);    
        }    
    }    
        
    @Override    
    public boolean onTouchEvent(MotionEvent ev) {    
        if (mGestureDetector != null) {    
            mGestureDetector.onTouchEvent(ev);    
        }    
        if ((mDragListener != null || mDropListener != null) && mDragView != null) {    
            int action = ev.getAction();     
            switch (action) {    
                case MotionEvent.ACTION_UP:    
                case MotionEvent.ACTION_CANCEL:    
                    Rect r = mTempRect;    
                    mDragView.getDrawingRect(r);    
                    stopDragging();    
                    if (mRemoveMode == SLIDE && ev.getX() > r.right * 3 / 4) {    
                        if (mRemoveListener != null) {    
                            mRemoveListener.remove(mFirstDragPos);    
                        }    
                        unExpandViews(true);    
                    } else {    
                        if (mDropListener != null && mDragPos >= 0 && mDragPos < getCount()) {    
                            mDropListener.drop(mFirstDragPos, mDragPos);    
                        }    
                        unExpandViews(false);    
                    }    
                    break;    
                        
                case MotionEvent.ACTION_DOWN:    
                case MotionEvent.ACTION_MOVE:    
                    int x = (int) ev.getX();    
                    int y = (int) ev.getY();    
                    dragView(x, y);    
                    int itemnum = getItemForPosition(y);    
                    if (itemnum >= 0) {    
                        if (action == MotionEvent.ACTION_DOWN || itemnum != mDragPos) {    
                            if (mDragListener != null) {    
                                mDragListener.drag(mDragPos, itemnum);    
                            }    
                            mDragPos = itemnum;    
                            doExpansion();    
                        }    
                        int speed = 0;    
                        adjustScrollBounds(y);    
                        if (y > mLowerBound) {    
                            // scroll the list up a bit    
                            speed = y > (mHeight + mLowerBound) / 2   16 : 4;    
                        } else if (y < mUpperBound) {    
                            // scroll the list down a bit    
                            speed = y < mUpperBound / 2   -16 : -4;    
                        }    
                        if (speed != 0) {    
                            int ref = pointToPosition(0, mHeight / 2);    
                            if (ref == AdapterView.INVALID_POSITION) {    
                                //we hit a divider or an invisible view, check somewhere else    
                                ref = pointToPosition(0, mHeight / 2 + getDividerHeight() + 64);    
                            }    
                            View v = getChildAt(ref - getFirstVisiblePosition());    
                            if (v!= null) {    
                                int pos = v.getTop();    
                                setSelectionFromTop(ref, pos - speed);    
                            }    
                        }    
                    }    
                    break;    
            }    
            return true;    
        }    
        return super.onTouchEvent(ev);    
    }    
        
    private void startDragging(Bitmap bm, int y) {    
        stopDragging();    
        mWindowParams = new WindowManager.LayoutParams();    
        mWindowParams.gravity = Gravity.TOP;    
        mWindowParams.x = 0;    
        mWindowParams.y = y - mDragPoint + mCoordOffset;    
        mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;    
        mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;    
        mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE    
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE    
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON    
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN    
                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;    
        mWindowParams.format = PixelFormat.TRANSLUCENT;    
        mWindowParams.windowAnimations = 0;    
            
        Context context = getContext();    
        ImageView v = new ImageView(context);    
//        int backGroundColor = context.getResources().getColor(R.color.dragndrop_background);    
//        v.setBackgroundColor(backGroundColor);    
        v.setImageBitmap(bm);    
        mDragBitmap = bm;    
        mWindowManager = (WindowManager)context.getSystemService("window");    
        mWindowManager.addView(v, mWindowParams);    
        mDragView = v;    
    }    
        
    private void dragView(int x, int y) {    
        if (mRemoveMode == SLIDE) {    
            float alpha = 1.0f;    
            int width = mDragView.getWidth();    
            if (x > width / 2) {    
                alpha = ((float)(width - x)) / (width / 2);    
            }    
            mWindowParams.alpha = alpha;    
        }    
        if (mRemoveMode == FLING) {    
            mWindowParams.x = x;    
        }    
        mWindowParams.y = y - mDragPoint + mCoordOffset;    
        mWindowManager.updateViewLayout(mDragView, mWindowParams);    
    }    
        
    private void stopDragging() {    
        if (mDragView != null) {    
            WindowManager wm = (WindowManager)getContext().getSystemService("window");    
            wm.removeView(mDragView);    
            mDragView.setImageDrawable(null);    
            mDragView = null;    
        }    
        if (mDragBitmap != null) {    
            mDragBitmap.recycle();    
            mDragBitmap = null;    
        }    
    }    
        
    public void setDragListener(DragListener l) {    
        mDragListener = l;    
    }    
        
    public void setDropListener(DropListener l) {    
        mDropListener = l;    
    }    
        
    public void setRemoveListener(RemoveListener l) {    
        mRemoveListener = l;    
    }    
    public interface DragListener {    
        void drag(int from, int to);    
    }    
    public interface DropListener {    
        void drop(int from, int to);    
    }    
    public interface RemoveListener {    
        void remove(int which);    
    }    
}    

 com.and.DragListview.TouchInterceptor 
        android:id=”@android:id/list” 
        android:layout_width=”match_parent” 
        android:layout_height=”match_parent”        
        android:textSize=”18sp” 
        android:drawSelectorOnTop=”false” 
        android:fastScrollEnabled=”true” /> 

还有这个: 
http://www.eoeandroid.com/thread-61490-1-1.html

转自:http://gundumw100.iteye.com/blog/919325

 

(转)手动升级Eclipse的Ant插件

(转)手动升级Eclipse的Ant插件

 

今天打包Android sdk3.0的包,提示需要ant1.80以上的版本,安装的eclipse的默认版本是1.70,网上搜了一下知道了手动升级Ant的办法

 

ant 现在最新版为 1.7.1 ,使用它可以解决运行 test-java 时出现的 ***.properties can not found 的错误,但至于 process fork faild 的问题还没解决。

手动升级过程很简单:
1> 下载最新版的ant。下面是下载地址,选择 .zip archive: apache-ant-1.7.1-bin.zip 即可
http://ant.apache.org/bindownload.cgi

2> 手动覆盖旧版本的内容。找到插件目录:%eclipse%/plugins\org.apache.ant*** 目录,把其中的内容用最新版1.7.1的去替换,
很重要的是:1、保留META-INF文件夹;2、保留plugin.properties文件。

3> 验证安装成功。若是系统环境变量里设置了 eclipse 的插件里的 ant ,则可以直接运行 ant -version 得到
Apache Ant version 1.7.1 compiled on June 27 2008
显示安装成功。

此时运行 test-java 是会出现很多 [junit]… 的信息

 

http://hi.baidu.com/7090/blog/item/4989119595615b1d7bf480db.html

 

androidListView

/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package android.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.PixelFormat;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ColorDrawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.SparseBooleanArray;
import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.SoundEffectConstants;
import android.view.accessibility.AccessibilityEvent;

import com.google.android.collect.Lists;
import com.android.internal.R;

import java.util.ArrayList;

/*
* Implementation Notes:
*
* Some terminology:
*
* index - index of the items that are currently visible
* position - index of the items in the cursor
*/

/**
* A view that shows items in a vertically scrolling list. The items
* come from the {@link ListAdapter} associated with this view.
*
* @attr ref android.R.styleable#ListView_entries
* @attr ref android.R.styleable#ListView_divider
* @attr ref android.R.styleable#ListView_dividerHeight
* @attr ref android.R.styleable#ListView_choiceMode
* @attr ref android.R.styleable#ListView_headerDividersEnabled
* @attr ref android.R.styleable#ListView_footerDividersEnabled
*/
public class ListView extends AbsListView {
/**
* Used to indicate a no preference for a position type.
*/
static final int NO_POSITION = -1;

/**
* Normal list that does not indicate choices
*/
public static final int CHOICE_MODE_NONE = 0;

/**
* The list allows up to one choice
*/
public static final int CHOICE_MODE_SINGLE = 1;

/**
* The list allows multiple choices
*/
public static final int CHOICE_MODE_MULTIPLE = 2;

/**
* When arrow scrolling, ListView will never scroll more than this factor
* times the height of the list.
*/
private static final float MAX_SCROLL_FACTOR = 0.33f;

/**
* When arrow scrolling, need a certain amount of pixels to preview next
* items. This is usually the fading edge, but if that is small enough,
* we want to make sure we preview at least this many pixels.
*/
private static final int MIN_SCROLL_PREVIEW_PIXELS = 2;

/**
* A class that represents a fixed view in a list, for example a header at the top
* or a footer at the bottom.
*/
public class FixedViewInfo {
/** The view to add to the list */
public View view;
/** The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. */
public Object data;
/** <code>true</code> if the fixed view should be selectable in the list */
public boolean isSelectable;
}

private ArrayList<FixedViewInfo> mHeaderViewInfos = Lists.newArrayList();
private ArrayList<FixedViewInfo> mFooterViewInfos = Lists.newArrayList();

Drawable mDivider;
int mDividerHeight;

private boolean mIsCacheColorOpaque;
private boolean mDividerIsOpaque;
private boolean mClipDivider;

private boolean mHeaderDividersEnabled;
private boolean mFooterDividersEnabled;

private boolean mAreAllItemsSelectable = true;

private boolean mItemsCanFocus = false;

private int mChoiceMode = CHOICE_MODE_NONE;

private SparseBooleanArray mCheckStates;

// used for temporary calculations.
private final Rect mTempRect = new Rect();
private Paint mDividerPaint;

// the single allocated result per list view; kinda cheesey but avoids
// allocating these thingies too often.
private ArrowScrollFocusResult mArrowScrollFocusResult = new ArrowScrollFocusResult();

public ListView(Context context) {
this(context, null);
}

public ListView(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.listViewStyle);
}

public ListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);

TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.ListView, defStyle, 0);

CharSequence[] entries = a.getTextArray(
com.android.internal.R.styleable.ListView_entries);
if (entries != null) {
setAdapter(new ArrayAdapter<CharSequence>(context,
com.android.internal.R.layout.simple_list_item_1, entries));
}

final Drawable d = a.getDrawable(com.android.internal.R.styleable.ListView_divider);
if (d != null) {
// If a divider is specified use its intrinsic height for divider height
setDivider(d);
}

// Use the height specified, zero being the default
final int dividerHeight = a.getDimensionPixelSize(
com.android.internal.R.styleable.ListView_dividerHeight, 0);
if (dividerHeight != 0) {
setDividerHeight(dividerHeight);
}

setChoiceMode(a.getInt(R.styleable.ListView_choiceMode, CHOICE_MODE_NONE));

mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true);
mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true);

a.recycle();
}

/**
* @return The maximum amount a list view will scroll in response to
* an arrow event.
*/
public int getMaxScrollAmount() {
return (int) (MAX_SCROLL_FACTOR * (mBottom - mTop));
}

/**
* Make sure views are touching the top or bottom edge, as appropriate for
* our gravity
*/
private void adjustViewsUpOrDown() {
final int childCount = getChildCount();
int delta;

if (childCount > 0) {
View child;

if (!mStackFromBottom) {
// Uh-oh -- we came up short. Slide all views up to make them
// align with the top
child = getChildAt(0);
delta = child.getTop() - mListPadding.top;
if (mFirstPosition != 0) {
// It's OK to have some space above the first item if it is
// part of the vertical spacing
delta -= mDividerHeight;
}
if (delta < 0) {
// We only are looking to see if we are too low, not too high
delta = 0;
}
} else {
// we are too high, slide all views down to align with bottom
child = getChildAt(childCount - 1);
delta = child.getBottom() - (getHeight() - mListPadding.bottom);

if (mFirstPosition + childCount < mItemCount) {
// It's OK to have some space below the last item if it is
// part of the vertical spacing
delta += mDividerHeight;
}

if (delta > 0) {
delta = 0;
}
}

if (delta != 0) {
offsetChildrenTopAndBottom(-delta);
}
}
}

/**
* Add a fixed view to appear at the top of the list. If addHeaderView is
* called more than once, the views will appear in the order they were
* added. Views added using this call can take focus if they want.
* <p>
* NOTE: Call this before calling setAdapter. This is so ListView can wrap
* the supplied cursor with one that that will also account for header
* views.
*
* @param v The view to add.
* @param data Data to associate with this view
* @param isSelectable whether the item is selectable
*/
public void addHeaderView(View v, Object data, boolean isSelectable) {

if (mAdapter != null) {
throw new IllegalStateException(
"Cannot add header view to list -- setAdapter has already been called.");
}

FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
info.isSelectable = isSelectable;
mHeaderViewInfos.add(info);
}

/**
* Add a fixed view to appear at the top of the list. If addHeaderView is
* called more than once, the views will appear in the order they were
* added. Views added using this call can take focus if they want.
* <p>
* NOTE: Call this before calling setAdapter. This is so ListView can wrap
* the supplied cursor with one that that will also account for header
* views.
*
* @param v The view to add.
*/
public void addHeaderView(View v) {
addHeaderView(v, null, true);
}

@Override
public int getHeaderViewsCount() {
return mHeaderViewInfos.size();
}

/**
* Removes a previously-added header view.
*
* @param v The view to remove
* @return true if the view was removed, false if the view was not a header
* view
*/
public boolean removeHeaderView(View v) {
if (mHeaderViewInfos.size() > 0) {
boolean result = false;
if (((HeaderViewListAdapter) mAdapter).removeHeader(v)) {
mDataSetObserver.onChanged();
result = true;
}
removeFixedViewInfo(v, mHeaderViewInfos);
return result;
}
return false;
}

private void removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) {
int len = where.size();
for (int i = 0; i < len; ++i) {
FixedViewInfo info = where.get(i);
if (info.view == v) {
where.remove(i);
break;
}
}
}

/**
* Add a fixed view to appear at the bottom of the list. If addFooterView is
* called more than once, the views will appear in the order they were
* added. Views added using this call can take focus if they want.
* <p>
* NOTE: Call this before calling setAdapter. This is so ListView can wrap
* the supplied cursor with one that that will also account for header
* views.
*
* @param v The view to add.
* @param data Data to associate with this view
* @param isSelectable true if the footer view can be selected
*/
public void addFooterView(View v, Object data, boolean isSelectable) {
FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
info.isSelectable = isSelectable;
mFooterViewInfos.add(info);

// in the case of re-adding a footer view, or adding one later on,
// we need to notify the observer
if (mDataSetObserver != null) {
mDataSetObserver.onChanged();
}
}

/**
* Add a fixed view to appear at the bottom of the list. If addFooterView is called more
* than once, the views will appear in the order they were added. Views added using
* this call can take focus if they want.
* <p>NOTE: Call this before calling setAdapter. This is so ListView can wrap the supplied
* cursor with one that that will also account for header views.
*
*
* @param v The view to add.
*/
public void addFooterView(View v) {
addFooterView(v, null, true);
}

@Override
public int getFooterViewsCount() {
return mFooterViewInfos.size();
}

/**
* Removes a previously-added footer view.
*
* @param v The view to remove
* @return
* true if the view was removed, false if the view was not a footer view
*/
public boolean removeFooterView(View v) {
if (mFooterViewInfos.size() > 0) {
boolean result = false;
if (((HeaderViewListAdapter) mAdapter).removeFooter(v)) {
mDataSetObserver.onChanged();
result = true;
}
removeFixedViewInfo(v, mFooterViewInfos);
return result;
}
return false;
}

/**
* Returns the adapter currently in use in this ListView. The returned adapter
* might not be the same adapter passed to {@link #setAdapter(ListAdapter)} but
* might be a {@link WrapperListAdapter}.
*
* @return The adapter currently used to display data in this ListView.
*
* @see #setAdapter(ListAdapter)
*/
@Override
public ListAdapter getAdapter() {
return mAdapter;
}

/**
* Sets the data behind this ListView.
*
* The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},
* depending on the ListView features currently in use. For instance, adding
* headers and/or footers will cause the adapter to be wrapped.
*
* @param adapter The ListAdapter which is responsible for maintaining the
* data backing this list and for producing a view to represent an
* item in that data set.
*
* @see #getAdapter()
*/
@Override
public void setAdapter(ListAdapter adapter) {
if (null != mAdapter) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}

resetList();
mRecycler.clear();

if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}

mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();

mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);

mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());

int position;
if (mStackFromBottom) {
position = lookForSelectablePosition(mItemCount - 1, false);
} else {
position = lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);

if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}

} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}

if (mCheckStates != null) {
mCheckStates.clear();
}

requestLayout();
}

/**
* The list is empty. Clear everything out.
*/
@Override
void resetList() {
// The parent's resetList() will remove all views from the layout so we need to
// cleanup the state of our footers and headers
clearRecycledState(mHeaderViewInfos);
clearRecycledState(mFooterViewInfos);

super.resetList();

mLayoutMode = LAYOUT_NORMAL;
}

private void clearRecycledState(ArrayList<FixedViewInfo> infos) {
if (infos != null) {
final int count = infos.size();

for (int i = 0; i < count; i++) {
final View child = infos.get(i).view;
final LayoutParams p = (LayoutParams) child.getLayoutParams();
if (p != null) {
p.recycledHeaderFooter = false;
}
}
}
}

/**
* @return Whether the list needs to show the top fading edge
*/
private boolean showingTopFadingEdge() {
final int listTop = mScrollY + mListPadding.top;
return (mFirstPosition > 0) || (getChildAt(0).getTop() > listTop);
}

/**
* @return Whether the list needs to show the bottom fading edge
*/
private boolean showingBottomFadingEdge() {
final int childCount = getChildCount();
final int bottomOfBottomChild = getChildAt(childCount - 1).getBottom();
final int lastVisiblePosition = mFirstPosition + childCount - 1;

final int listBottom = mScrollY + getHeight() - mListPadding.bottom;

return (lastVisiblePosition < mItemCount - 1)
|| (bottomOfBottomChild < listBottom);
}

@Override
public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {

int rectTopWithinChild = rect.top;

// offset so rect is in coordinates of the this view
rect.offset(child.getLeft(), child.getTop());
rect.offset(-child.getScrollX(), -child.getScrollY());

final int height = getHeight();
int listUnfadedTop = getScrollY();
int listUnfadedBottom = listUnfadedTop + height;
final int fadingEdge = getVerticalFadingEdgeLength();

if (showingTopFadingEdge()) {
// leave room for top fading edge as long as rect isn't at very top
if ((mSelectedPosition > 0) || (rectTopWithinChild > fadingEdge)) {
listUnfadedTop += fadingEdge;
}
}

int childCount = getChildCount();
int bottomOfBottomChild = getChildAt(childCount - 1).getBottom();

if (showingBottomFadingEdge()) {
// leave room for bottom fading edge as long as rect isn't at very bottom
if ((mSelectedPosition < mItemCount - 1)
|| (rect.bottom < (bottomOfBottomChild - fadingEdge))) {
listUnfadedBottom -= fadingEdge;
}
}

int scrollYDelta = 0;

if (rect.bottom > listUnfadedBottom && rect.top > listUnfadedTop) {
// need to MOVE DOWN to get it in view: move down just enough so
// that the entire rectangle is in view (or at least the first
// screen size chunk).

if (rect.height() > height) {
// just enough to get screen size chunk on
scrollYDelta += (rect.top - listUnfadedTop);
} else {
// get entire rect at bottom of screen
scrollYDelta += (rect.bottom - listUnfadedBottom);
}

// make sure we aren't scrolling beyond the end of our children
int distanceToBottom = bottomOfBottomChild - listUnfadedBottom;
scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
} else if (rect.top < listUnfadedTop && rect.bottom < listUnfadedBottom) {
// need to MOVE UP to get it in view: move up just enough so that
// entire rectangle is in view (or at least the first screen
// size chunk of it).

if (rect.height() > height) {
// screen size chunk
scrollYDelta -= (listUnfadedBottom - rect.bottom);
} else {
// entire rect at top
scrollYDelta -= (listUnfadedTop - rect.top);
}

// make sure we aren't scrolling any further than the top our children
int top = getChildAt(0).getTop();
int deltaToTop = top - listUnfadedTop;
scrollYDelta = Math.max(scrollYDelta, deltaToTop);
}

final boolean scroll = scrollYDelta != 0;
if (scroll) {
scrollListItemsBy(-scrollYDelta);
positionSelector(child);
mSelectedTop = child.getTop();
invalidate();
}
return scroll;
}

/**
* {@inheritDoc}
*/
@Override
void fillGap(boolean down) {
final int count = getChildCount();
if (down) {
final int startOffset = count > 0   getChildAt(count - 1).getBottom() + mDividerHeight :
getListPaddingTop();
fillDown(mFirstPosition + count, startOffset);
correctTooHigh(getChildCount());
} else {
final int startOffset = count > 0   getChildAt(0).getTop() - mDividerHeight :
getHeight() - getListPaddingBottom();
fillUp(mFirstPosition - 1, startOffset);
correctTooLow(getChildCount());
}
}

/**
* Fills the list from pos down to the end of the list view.
*
* @param pos The first position to put in the list
*
* @param nextTop The location where the top of the item associated with pos
* should be drawn
*
* @return The view that is currently selected, if it happens to be in the
* range that we draw.
*/
private View fillDown(int pos, int nextTop) {
View selectedView = null;

int end = (mBottom - mTop) - mListPadding.bottom;

while (nextTop < end && pos < mItemCount) {
// is this the selected item 
boolean selected = pos == mSelectedPosition;
View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected);

nextTop = child.getBottom() + mDividerHeight;
if (selected) {
selectedView = child;
}
pos++;
}

return selectedView;
}

/**
* Fills the list from pos up to the top of the list view.
*
* @param pos The first position to put in the list
*
* @param nextBottom The location where the bottom of the item associated
* with pos should be drawn
*
* @return The view that is currently selected
*/
private View fillUp(int pos, int nextBottom) {
View selectedView = null;

int end = mListPadding.top;

while (nextBottom > end && pos >= 0) {
// is this the selected item 
boolean selected = pos == mSelectedPosition;
View child = makeAndAddView(pos, nextBottom, false, mListPadding.left, selected);
nextBottom = child.getTop() - mDividerHeight;
if (selected) {
selectedView = child;
}
pos--;
}

mFirstPosition = pos + 1;

return selectedView;
}

/**
* Fills the list from top to bottom, starting with mFirstPosition
*
* @param nextTop The location where the top of the first item should be
* drawn
*
* @return The view that is currently selected
*/
private View fillFromTop(int nextTop) {
mFirstPosition = Math.min(mFirstPosition, mSelectedPosition);
mFirstPosition = Math.min(mFirstPosition, mItemCount - 1);
if (mFirstPosition < 0) {
mFirstPosition = 0;
}
return fillDown(mFirstPosition, nextTop);
}

/**
* Put mSelectedPosition in the middle of the screen and then build up and
* down from there. This method forces mSelectedPosition to the center.
*
* @param childrenTop Top of the area in which children can be drawn, as
* measured in pixels
* @param childrenBottom Bottom of the area in which children can be drawn,
* as measured in pixels
* @return Currently selected view
*/
private View fillFromMiddle(int childrenTop, int childrenBottom) {
int height = childrenBottom - childrenTop;

int position = reconcileSelectedPosition();

View sel = makeAndAddView(position, childrenTop, true,
mListPadding.left, true);
mFirstPosition = position;

int selHeight = sel.getMeasuredHeight();
if (selHeight <= height) {
sel.offsetTopAndBottom((height - selHeight) / 2);
}

fillAboveAndBelow(sel, position);

if (!mStackFromBottom) {
correctTooHigh(getChildCount());
} else {
correctTooLow(getChildCount());
}

return sel;
}

/**
* Once the selected view as been placed, fill up the visible area above and
* below it.
*
* @param sel The selected view
* @param position The position corresponding to sel
*/
private void fillAboveAndBelow(View sel, int position) {
final int dividerHeight = mDividerHeight;
if (!mStackFromBottom) {
fillUp(position - 1, sel.getTop() - dividerHeight);
adjustViewsUpOrDown();
fillDown(position + 1, sel.getBottom() + dividerHeight);
} else {
fillDown(position + 1, sel.getBottom() + dividerHeight);
adjustViewsUpOrDown();
fillUp(position - 1, sel.getTop() - dividerHeight);
}
}

/**
* Fills the grid based on positioning the new selection at a specific
* location. The selection may be moved so that it does not intersect the
* faded edges. The grid is then filled upwards and downwards from there.
*
* @param selectedTop Where the selected item should be
* @param childrenTop Where to start drawing children
* @param childrenBottom Last pixel where children can be drawn
* @return The view that currently has selection
*/
private View fillFromSelection(int selectedTop, int childrenTop, int childrenBottom) {
int fadingEdgeLength = getVerticalFadingEdgeLength();
final int selectedPosition = mSelectedPosition;

View sel;

final int topSelectionPixel = getTopSelectionPixel(childrenTop, fadingEdgeLength,
selectedPosition);
final int bottomSelectionPixel = getBottomSelectionPixel(childrenBottom, fadingEdgeLength,
selectedPosition);

sel = makeAndAddView(selectedPosition, selectedTop, true, mListPadding.left, true);

// Some of the newly selected item extends below the bottom of the list
if (sel.getBottom() > bottomSelectionPixel) {
// Find space available above the selection into which we can scroll
// upwards
final int spaceAbove = sel.getTop() - topSelectionPixel;

// Find space required to bring the bottom of the selected item
// fully into view
final int spaceBelow = sel.getBottom() - bottomSelectionPixel;
final int offset = Math.min(spaceAbove, spaceBelow);

// Now offset the selected item to get it into view
sel.offsetTopAndBottom(-offset);
} else if (sel.getTop() < topSelectionPixel) {
// Find space required to bring the top of the selected item fully
// into view
final int spaceAbove = topSelectionPixel - sel.getTop();

// Find space available below the selection into which we can scroll
// downwards
final int spaceBelow = bottomSelectionPixel - sel.getBottom();
final int offset = Math.min(spaceAbove, spaceBelow);

// Offset the selected item to get it into view
sel.offsetTopAndBottom(offset);
}

// Fill in views above and below
fillAboveAndBelow(sel, selectedPosition);

if (!mStackFromBottom) {
correctTooHigh(getChildCount());
} else {
correctTooLow(getChildCount());
}

return sel;
}

/**
* Calculate the bottom-most pixel we can draw the selection into
*
* @param childrenBottom Bottom pixel were children can be drawn
* @param fadingEdgeLength Length of the fading edge in pixels, if present
* @param selectedPosition The position that will be selected
* @return The bottom-most pixel we can draw the selection into
*/
private int getBottomSelectionPixel(int childrenBottom, int fadingEdgeLength,
int selectedPosition) {
int bottomSelectionPixel = childrenBottom;
if (selectedPosition != mItemCount - 1) {
bottomSelectionPixel -= fadingEdgeLength;
}
return bottomSelectionPixel;
}

/**
* Calculate the top-most pixel we can draw the selection into
*
* @param childrenTop Top pixel were children can be drawn
* @param fadingEdgeLength Length of the fading edge in pixels, if present
* @param selectedPosition The position that will be selected
* @return The top-most pixel we can draw the selection into
*/
private int getTopSelectionPixel(int childrenTop, int fadingEdgeLength, int selectedPosition) {
// first pixel we can draw the selection into
int topSelectionPixel = childrenTop;
if (selectedPosition > 0) {
topSelectionPixel += fadingEdgeLength;
}
return topSelectionPixel;
}

/**
* Fills the list based on positioning the new selection relative to the old
* selection. The new selection will be placed at, above, or below the
* location of the new selection depending on how the selection is moving.
* The selection will then be pinned to the visible part of the screen,
* excluding the edges that are faded. The list is then filled upwards and
* downwards from there.
*
* @param oldSel The old selected view. Useful for trying to put the new
* selection in the same place
* @param newSel The view that is to become selected. Useful for trying to
* put the new selection in the same place
* @param delta Which way we are moving
* @param childrenTop Where to start drawing children
* @param childrenBottom Last pixel where children can be drawn
* @return The view that currently has selection
*/
private View moveSelection(View oldSel, View newSel, int delta, int childrenTop,
int childrenBottom) {
int fadingEdgeLength = getVerticalFadingEdgeLength();
final int selectedPosition = mSelectedPosition;

View sel;

final int topSelectionPixel = getTopSelectionPixel(childrenTop, fadingEdgeLength,
selectedPosition);
final int bottomSelectionPixel = getBottomSelectionPixel(childrenTop, fadingEdgeLength,
selectedPosition);

if (delta > 0) {
/*
* Case 1: Scrolling down.
*/

/*
* Before After
* | | | |
* +-------+ +-------+
* | A | | A |
* | 1 | => +-------+
* +-------+ | B |
* | B | | 2 |
* +-------+ +-------+
* | | | |
*
* Try to keep the top of the previously selected item where it was.
* oldSel = A
* sel = B
*/

// Put oldSel (A) where it belongs
oldSel = makeAndAddView(selectedPosition - 1, oldSel.getTop(), true,
mListPadding.left, false);

final int dividerHeight = mDividerHeight;

// Now put the new selection (B) below that
sel = makeAndAddView(selectedPosition, oldSel.getBottom() + dividerHeight, true,
mListPadding.left, true);

// Some of the newly selected item extends below the bottom of the list
if (sel.getBottom() > bottomSelectionPixel) {

// Find space available above the selection into which we can scroll upwards
int spaceAbove = sel.getTop() - topSelectionPixel;

// Find space required to bring the bottom of the selected item fully into view
int spaceBelow = sel.getBottom() - bottomSelectionPixel;

// Don't scroll more than half the height of the list
int halfVerticalSpace = (childrenBottom - childrenTop) / 2;
int offset = Math.min(spaceAbove, spaceBelow);
offset = Math.min(offset, halfVerticalSpace);

// We placed oldSel, so offset that item
oldSel.offsetTopAndBottom(-offset);
// Now offset the selected item to get it into view
sel.offsetTopAndBottom(-offset);
}

// Fill in views above and below
if (!mStackFromBottom) {
fillUp(mSelectedPosition - 2, sel.getTop() - dividerHeight);
adjustViewsUpOrDown();
fillDown(mSelectedPosition + 1, sel.getBottom() + dividerHeight);
} else {
fillDown(mSelectedPosition + 1, sel.getBottom() + dividerHeight);
adjustViewsUpOrDown();
fillUp(mSelectedPosition - 2, sel.getTop() - dividerHeight);
}
} else if (delta < 0) {
/*
* Case 2: Scrolling up.
*/

/*
* Before After
* | | | |
* +-------+ +-------+
* | A | | A |
* +-------+ => | 1 |
* | B | +-------+
* | 2 | | B |
* +-------+ +-------+
* | | | |
*
* Try to keep the top of the item about to become selected where it was.
* newSel = A
* olSel = B
*/

if (newSel != null) {
// Try to position the top of newSel (A) where it was before it was selected
sel = makeAndAddView(selectedPosition, newSel.getTop(), true, mListPadding.left,
true);
} else {
// If (A) was not on screen and so did not have a view, position
// it above the oldSel (B)
sel = makeAndAddView(selectedPosition, oldSel.getTop(), false, mListPadding.left,
true);
}

// Some of the newly selected item extends above the top of the list
if (sel.getTop() < topSelectionPixel) {
// Find space required to bring the top of the selected item fully into view
int spaceAbove = topSelectionPixel - sel.getTop();

// Find space available below the selection into which we can scroll downwards
int spaceBelow = bottomSelectionPixel - sel.getBottom();

// Don't scroll more than half the height of the list
int halfVerticalSpace = (childrenBottom - childrenTop) / 2;
int offset = Math.min(spaceAbove, spaceBelow);
offset = Math.min(offset, halfVerticalSpace);

// Offset the selected item to get it into view
sel.offsetTopAndBottom(offset);
}

// Fill in views above and below
fillAboveAndBelow(sel, selectedPosition);
} else {

int oldTop = oldSel.getTop();

/*
* Case 3: Staying still
*/
sel = makeAndAddView(selectedPosition, oldTop, true, mListPadding.left, true);

// We're staying still...
if (oldTop < childrenTop) {
// ... but the top of the old selection was off screen.
// (This can happen if the data changes size out from under us)
int newBottom = sel.getBottom();
if (newBottom < childrenTop + 20) {
// Not enough visible -- bring it onscreen
sel.offsetTopAndBottom(childrenTop - sel.getTop());
}
}

// Fill in views above and below
fillAboveAndBelow(sel, selectedPosition);
}

return sel;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Sets up mListPadding
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);

int childWidth = 0;
int childHeight = 0;

mItemCount = mAdapter == null   0 : mAdapter.getCount();
if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED ||
heightMode == MeasureSpec.UNSPECIFIED)) {
final View child = obtainView(0);

measureScrapChild(child, 0, widthMeasureSpec);

childWidth = child.getMeasuredWidth();
childHeight = child.getMeasuredHeight();

if (recycleOnMeasure()) {
mRecycler.addScrapView(child);
}
}

if (widthMode == MeasureSpec.UNSPECIFIED) {
widthSize = mListPadding.left + mListPadding.right + childWidth +
getVerticalScrollbarWidth();
}

if (heightMode == MeasureSpec.UNSPECIFIED) {
heightSize = mListPadding.top + mListPadding.bottom + childHeight +
getVerticalFadingEdgeLength() * 2;
}

if (heightMode == MeasureSpec.AT_MOST) {
// TODO: after first layout we should maybe start at the first visible position, not 0
heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
}

setMeasuredDimension(widthSize, heightSize);
mWidthMeasureSpec = widthMeasureSpec;
}

private void measureScrapChild(View child, int position, int widthMeasureSpec) {
LayoutParams p = (LayoutParams) child.getLayoutParams();
if (p == null) {
p = new LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0);
child.setLayoutParams(p);
}
p.viewType = mAdapter.getItemViewType(position);

int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec,
mListPadding.left + mListPadding.right, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
}

/**
* @return True to recycle the views used to measure this ListView in
* UNSPECIFIED/AT_MOST modes, false otherwise.
* @hide
*/
@ViewDebug.ExportedProperty
protected boolean recycleOnMeasure() {
return true;
}

/**
* Measures the height of the given range of children (inclusive) and
* returns the height with this ListView's padding and divider heights
* included. If maxHeight is provided, the measuring will stop when the
* current height reaches maxHeight.
*
* @param widthMeasureSpec The width measure spec to be given to a child's
* {@link View#measure(int, int)}.
* @param startPosition The position of the first child to be shown.
* @param endPosition The (inclusive) position of the last child to be
* shown. Specify {@link #NO_POSITION} if the last child should be
* the last available child from the adapter.
* @param maxHeight The maximum height that will be returned (if all the
* children don't fit in this value, this value will be
* returned).
* @param disallowPartialChildPosition In general, whether the returned
* height should only contain entire children. This is more
* powerful--it is the first inclusive position at which partial
* children will not be allowed. Example: it looks nice to have
* at least 3 completely visible children, and in portrait this
* will most likely fit; but in landscape there could be times
* when even 2 children can not be completely shown, so a value
* of 2 (remember, inclusive) would be good (assuming
* startPosition is 0).
* @return The height of this ListView with the given children.
*/
final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,
final int maxHeight, int disallowPartialChildPosition) {

final ListAdapter adapter = mAdapter;
if (adapter == null) {
return mListPadding.top + mListPadding.bottom;
}

// Include the padding of the list
int returnedHeight = mListPadding.top + mListPadding.bottom;
final int dividerHeight = ((mDividerHeight > 0) && mDivider != null)   mDividerHeight : 0;
// The previous height value that was less than maxHeight and contained
// no partial children
int prevHeightWithoutPartialChild = 0;
int i;
View child;

// mItemCount - 1 since endPosition parameter is inclusive
endPosition = (endPosition == NO_POSITION)   adapter.getCount() - 1 : endPosition;
final AbsListView.RecycleBin recycleBin = mRecycler;
final boolean recyle = recycleOnMeasure();

for (i = startPosition; i <= endPosition; ++i) {
child = obtainView(i);

measureScrapChild(child, i, widthMeasureSpec);

if (i > 0) {
// Count the divider for all but one child
returnedHeight += dividerHeight;
}

// Recycle the view before we possibly return from the method
if (recyle) {
recycleBin.addScrapView(child);
}

returnedHeight += child.getMeasuredHeight();

if (returnedHeight >= maxHeight) {
// We went over, figure out which height to return. If returnedHeight > maxHeight,
// then the i'th position did not fit completely.
return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)
&& (i > disallowPartialChildPosition) // We've past the min pos
&& (prevHeightWithoutPartialChild > 0) // We have a prev height
&& (returnedHeight != maxHeight) // i'th child did not fit completely
  prevHeightWithoutPartialChild
: maxHeight;
}

if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {
prevHeightWithoutPartialChild = returnedHeight;
}
}

// At this point, we went through the range of children, and they each
// completely fit, so return the returnedHeight
return returnedHeight;
}

@Override
int findMotionRow(int y) {
int childCount = getChildCount();
if (childCount > 0) {
for (int i = 0; i < childCount; i++) {
View v = getChildAt(i);
if (y <= v.getBottom()) {
return mFirstPosition + i;
}
}
return mFirstPosition + childCount - 1;
}
return INVALID_POSITION;
}

/**
* Put a specific item at a specific location on the screen and then build
* up and down from there.
*
* @param position The reference view to use as the starting point
* @param top Pixel offset from the top of this view to the top of the
* reference view.
*
* @return The selected view, or null if the selected view is outside the
* visible area.
*/
private View fillSpecific(int position, int top) {
boolean tempIsSelected = position == mSelectedPosition;
View temp = makeAndAddView(position, top, true, mListPadding.left, tempIsSelected);
// Possibly changed again in fillUp if we add rows above this one.
mFirstPosition = position;

View above;
View below;

final int dividerHeight = mDividerHeight;
if (!mStackFromBottom) {
above = fillUp(position - 1, temp.getTop() - dividerHeight);
// This will correct for the top of the first view not touching the top of the list
adjustViewsUpOrDown();
below = fillDown(position + 1, temp.getBottom() + dividerHeight);
int childCount = getChildCount();
if (childCount > 0) {
correctTooHigh(childCount);
}
} else {
below = fillDown(position + 1, temp.getBottom() + dividerHeight);
// This will correct for the bottom of the last view not touching the bottom of the list
adjustViewsUpOrDown();
above = fillUp(position - 1, temp.getTop() - dividerHeight);
int childCount = getChildCount();
if (childCount > 0) {
correctTooLow(childCount);
}
}

if (tempIsSelected) {
return temp;
} else if (above != null) {
return above;
} else {
return below;
}
}

/**
* Check if we have dragged the bottom of the list too high (we have pushed the
* top element off the top of the screen when we did not need to). Correct by sliding
* everything back down.
*
* @param childCount Number of children
*/
private void correctTooHigh(int childCount) {
// First see if the last item is visible. If it is not, it is OK for the
// top of the list to be pushed up.
int lastPosition = mFirstPosition + childCount - 1;
if (lastPosition == mItemCount - 1 && childCount > 0) {

// Get the last child ...
final View lastChild = getChildAt(childCount - 1);

// ... and its bottom edge
final int lastBottom = lastChild.getBottom();

// This is bottom of our drawable area
final int end = (mBottom - mTop) - mListPadding.bottom;

// This is how far the bottom edge of the last view is from the bottom of the
// drawable area
int bottomOffset = end - lastBottom;
View firstChild = getChildAt(0);
final int firstTop = firstChild.getTop();

// Make sure we are 1) Too high, and 2) Either there are more rows above the
// first row or the first row is scrolled off the top of the drawable area
if (bottomOffset > 0 && (mFirstPosition > 0 || firstTop < mListPadding.top)) {
if (mFirstPosition == 0) {
// Don't pull the top too far down
bottomOffset = Math.min(bottomOffset, mListPadding.top - firstTop);
}
// Move everything down
offsetChildrenTopAndBottom(bottomOffset);
if (mFirstPosition > 0) {
// Fill the gap that was opened above mFirstPosition with more rows, if
// possible
fillUp(mFirstPosition - 1, firstChild.getTop() - mDividerHeight);
// Close up the remaining gap
adjustViewsUpOrDown();
}

}
}
}

/**
* Check if we have dragged the bottom of the list too low (we have pushed the
* bottom element off the bottom of the screen when we did not need to). Correct by sliding
* everything back up.
*
* @param childCount Number of children
*/
private void correctTooLow(int childCount) {
// First see if the first item is visible. If it is not, it is OK for the
// bottom of the list to be pushed down.
if (mFirstPosition == 0 && childCount > 0) {

// Get the first child ...
final View firstChild = getChildAt(0);

// ... and its top edge
final int firstTop = firstChild.getTop();

// This is top of our drawable area
final int start = mListPadding.top;

// This is bottom of our drawable area
final int end = (mBottom - mTop) - mListPadding.bottom;

// This is how far the top edge of the first view is from the top of the
// drawable area
int topOffset = firstTop - start;
View lastChild = getChildAt(childCount - 1);
final int lastBottom = lastChild.getBottom();
int lastPosition = mFirstPosition + childCount - 1;

// Make sure we are 1) Too low, and 2) Either there are more rows below the
// last row or the last row is scrolled off the bottom of the drawable area
if (topOffset > 0) {
if (lastPosition < mItemCount - 1 || lastBottom > end) {
if (lastPosition == mItemCount - 1) {
// Don't pull the bottom too far up
topOffset = Math.min(topOffset, lastBottom - end);
}
// Move everything up
offsetChildrenTopAndBottom(-topOffset);
if (lastPosition < mItemCount - 1) {
// Fill the gap that was opened below the last position with more rows, if
// possible
fillDown(lastPosition + 1, lastChild.getBottom() + mDividerHeight);
// Close up the remaining gap
adjustViewsUpOrDown();
}
} else if (lastPosition == mItemCount - 1) {
adjustViewsUpOrDown();
}
}
}
}

@Override
protected void layoutChildren() {
final boolean blockLayoutRequests = mBlockLayoutRequests;
if (!blockLayoutRequests) {
mBlockLayoutRequests = true;
} else {
return;
}

try {
super.layoutChildren();

invalidate();

if (mAdapter == null) {
resetList();
invokeOnItemScrollListener();
return;
}

int childrenTop = mListPadding.top;
int childrenBottom = mBottom - mTop - mListPadding.bottom;

int childCount = getChildCount();
int index;
int delta = 0;

View sel;
View oldSel = null;
View oldFirst = null;
View newSel = null;

View focusLayoutRestoreView = null;

// Remember stuff we will need down below
switch (mLayoutMode) {
case LAYOUT_SET_SELECTION:
index = mNextSelectedPosition - mFirstPosition;
if (index >= 0 && index < childCount) {
newSel = getChildAt(index);
}
break;
case LAYOUT_FORCE_TOP:
case LAYOUT_FORCE_BOTTOM:
case LAYOUT_SPECIFIC:
case LAYOUT_SYNC:
break;
case LAYOUT_MOVE_SELECTION:
default:
// Remember the previously selected view
index = mSelectedPosition - mFirstPosition;
if (index >= 0 && index < childCount) {
oldSel = getChildAt(index);
}

// Remember the previous first child
oldFirst = getChildAt(0);

if (mNextSelectedPosition >= 0) {
delta = mNextSelectedPosition - mSelectedPosition;
}

// Caution: newSel might be null
newSel = getChildAt(index + delta);
}

boolean dataChanged = mDataChanged;
if (dataChanged) {
handleDataChanged();
}

// Handle the empty set by removing all views that are visible
// and calling it a day
if (mItemCount == 0) {
resetList();
invokeOnItemScrollListener();
return;
} else if (mItemCount != mAdapter.getCount()) {
throw new IllegalStateException("The content of the adapter has changed but "
+ "ListView did not receive a notification. Make sure the content of "
+ "your adapter is not modified from a background thread, but only "
+ "from the UI thread. [in ListView(" + getId() + ", " + getClass()
+ ") with Adapter(" + mAdapter.getClass() + ")]");
}

setSelectedPositionInt(mNextSelectedPosition);

// Pull all children into the RecycleBin.
// These views will be reused if possible
final int firstPosition = mFirstPosition;
final RecycleBin recycleBin = mRecycler;

// reset the focus restoration
View focusLayoutRestoreDirectChild = null;

// Don't put header or footer views into the Recycler. Those are
// already cached in mHeaderViews;
if (dataChanged) {
for (int i = 0; i < childCount; i++) {
recycleBin.addScrapView(getChildAt(i));
if (ViewDebug.TRACE_RECYCLER) {
ViewDebug.trace(getChildAt(i),
ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, index, i);
}
}
} else {
recycleBin.fillActiveViews(childCount, firstPosition);
}

// take focus back to us temporarily to avoid the eventual
// call to clear focus when removing the focused child below
// from messing things up when ViewRoot assigns focus back
// to someone else
final View focusedChild = getFocusedChild();
if (focusedChild != null) {
// TODO: in some cases focusedChild.getParent() == null

// we can remember the focused view to restore after relayout if the
// data hasn't changed, or if the focused position is a header or footer
if (!dataChanged || isDirectChildHeaderOrFooter(focusedChild)) {
focusLayoutRestoreDirectChild = focusedChild;
// remember the specific view that had focus
focusLayoutRestoreView = findFocus();
if (focusLayoutRestoreView != null) {
// tell it we are going to mess with it
focusLayoutRestoreView.onStartTemporaryDetach();
}
}
requestFocus();
}

// Clear out old views
//removeAllViewsInLayout();
detachAllViewsFromParent();

switch (mLayoutMode) {
case LAYOUT_SET_SELECTION:
if (newSel != null) {
sel = fillFromSelection(newSel.getTop(), childrenTop, childrenBottom);
} else {
sel = fillFromMiddle(childrenTop, childrenBottom);
}
break;
case LAYOUT_SYNC:
sel = fillSpecific(mSyncPosition, mSpecificTop);
break;
case LAYOUT_FORCE_BOTTOM:
sel = fillUp(mItemCount - 1, childrenBottom);
adjustViewsUpOrDown();
break;
case LAYOUT_FORCE_TOP:
mFirstPosition = 0;
sel = fillFromTop(childrenTop);
adjustViewsUpOrDown();
break;
case LAYOUT_SPECIFIC:
sel = fillSpecific(reconcileSelectedPosition(), mSpecificTop);
break;
case LAYOUT_MOVE_SELECTION:
sel = moveSelection(oldSel, newSel, delta, childrenTop, childrenBottom);
break;
default:
if (childCount == 0) {
if (!mStackFromBottom) {
final int position = lookForSelectablePosition(0, true);
setSelectedPositionInt(position);
sel = fillFromTop(childrenTop);
} else {
final int position = lookForSelectablePosition(mItemCount - 1, false);
setSelectedPositionInt(position);
sel = fillUp(mItemCount - 1, childrenBottom);
}
} else {
if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) {
sel = fillSpecific(mSelectedPosition,
oldSel == null   childrenTop : oldSel.getTop());
} else if (mFirstPosition < mItemCount) {
sel = fillSpecific(mFirstPosition,
oldFirst == null   childrenTop : oldFirst.getTop());
} else {
sel = fillSpecific(0, childrenTop);
}
}
break;
}

// Flush any cached views that did not get reused above
recycleBin.scrapActiveViews();

if (sel != null) {
// the current selected item should get focus if items
// are focusable
if (mItemsCanFocus && hasFocus() && !sel.hasFocus()) {
final boolean focusWasTaken = (sel == focusLayoutRestoreDirectChild &&
focusLayoutRestoreView.requestFocus()) || sel.requestFocus();
if (!focusWasTaken) {
// selected item didn't take focus, fine, but still want
// to make sure something else outside of the selected view
// has focus
final View focused = getFocusedChild();
if (focused != null) {
focused.clearFocus();
}
positionSelector(sel);
} else {
sel.setSelected(false);
mSelectorRect.setEmpty();
}
} else {
positionSelector(sel);
}
mSelectedTop = sel.getTop();
} else {
if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) {
View child = getChildAt(mMotionPosition - mFirstPosition);
if (child != null) positionSelector(child);
} else {
mSelectedTop = 0;
mSelectorRect.setEmpty();
}

// even if there is not selected position, we may need to restore
// focus (i.e. something focusable in touch mode)
if (hasFocus() && focusLayoutRestoreView != null) {
focusLayoutRestoreView.requestFocus();
}
}

// tell focus view we are done mucking with it, if it is still in
// our view hierarchy.
if (focusLayoutRestoreView != null
&& focusLayoutRestoreView.getWindowToken() != null) {
focusLayoutRestoreView.onFinishTemporaryDetach();
}

mLayoutMode = LAYOUT_NORMAL;
mDataChanged = false;
mNeedSync = false;
setNextSelectedPositionInt(mSelectedPosition);

updateScrollIndicators();

if (mItemCount > 0) {
checkSelectionChanged();
}

invokeOnItemScrollListener();
} finally {
if (!blockLayoutRequests) {
mBlockLayoutRequests = false;
}
}
}

/**
* @param child a direct child of this list.
* @return Whether child is a header or footer view.
*/
private boolean isDirectChildHeaderOrFooter(View child) {

final ArrayList<FixedViewInfo> headers = mHeaderViewInfos;
final int numHeaders = headers.size();
for (int i = 0; i < numHeaders; i++) {
if (child == headers.get(i).view) {
return true;
}
}
final ArrayList<FixedViewInfo> footers = mFooterViewInfos;
final int numFooters = footers.size();
for (int i = 0; i < numFooters; i++) {
if (child == footers.get(i).view) {
return true;
}
}
return false;
}

/**
* Obtain the view and add it to our list of children. The view can be made
* fresh, converted from an unused view, or used as is if it was in the
* recycle bin.
*
* @param position Logical position in the list
* @param y Top or bottom edge of the view to add
* @param flow If flow is true, align top edge to y. If false, align bottom
* edge to y.
* @param childrenLeft Left edge where children should be positioned
* @param selected Is this position selected 
* @return View that was added
*/
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
boolean selected) {
View child;

if (!mDataChanged) {
// Try to use an exsiting view for this position
child = mRecycler.getActiveView(position);
if (child != null) {
if (ViewDebug.TRACE_RECYCLER) {
ViewDebug.trace(child, ViewDebug.RecyclerTraceType.RECYCLE_FROM_ACTIVE_HEAP,
position, getChildCount());
}

// Found it -- we're using an existing child
// This just needs to be positioned
setupChild(child, position, y, flow, childrenLeft, selected, true);

return child;
}
}

// Make a new view for this position, or convert an unused view if possible
child = obtainView(position);

// This needs to be positioned and measured
setupChild(child, position, y, flow, childrenLeft, selected, false);

return child;
}

/**
* Add a view as a child and make sure it is measured (if necessary) and
* positioned properly.
*
* @param child The view to add
* @param position The position of this child
* @param y The y po

2010.12.14———android应用的自动更新

2010.12.14——— android 应用的自动更新

参考:http://ming-fanglin.iteye.com/blog/795450

      http://www.androidres.com/index.php/2009/04/13/add-liveupdate-to-android-applications/

http://www.cnblogs.com/qianxudetianxia/archive/2011/04/26/2010930.html

看了网上的一些东西 自己觉得 思路大致分三步:

  • 1、检查是否更新
  • 2、下载最新apk文件
  • 3、更新应用

代码如下:

UpdateProject.java

package com.huitu.project;


import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.KeyEvent;

import com.huitu.util.HttpUtil;

public class UpdateProject extends Activity{
	private ProgressDialog pd;
	private Handler mProgressHandler = new Handler();
	private Intent intent;
	
	//private UninstallReceiver ur;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.updateproject);
		intent = new Intent(this,MainActivity.class);
		
		SharedPreferences sp = getSharedPreferences("version", Context.MODE_PRIVATE);
		
		long lastUpdateTime = sp.getLong("lastUpdateTime", System.currentTimeMillis());
		
		//更新间隔至少一天
		//if(lastUpdateTime + (24*60*60*1000)<System.currentTimeMillis()){
			lastUpdateTime = System.currentTimeMillis();
			Editor editor = sp.edit();
			editor.putLong("lastUpdateTime", lastUpdateTime);
			editor.commit();
			
			if(checkUpdate()){
				Dialog dialog = new AlertDialog.Builder(this).setTitle("系统更新")
					.setMessage("已有新版本,是否更新?")
					.setPositiveButton("确定",new DialogInterface.OnClickListener() {
						
						public void onClick(DialogInterface dialog, int which) {
							pd = new ProgressDialog(UpdateProject.this);
							pd.setMessage("正在更新,请稍后...");
							pd.show();
							down(HttpUtil.BASE_URL+"android_file/cpjw.apk");
							
						}
					})
					.setNegativeButton("取消", new DialogInterface.OnClickListener() {
						
						public void onClick(DialogInterface dialog, int which) {
							startActivity(intent);
						}
					}).create();
				dialog.show();
					
			}else{
				startActivity(intent);
			}
			
//		}else{
//			startActivity(intent);
//		}
		
	}
	
	//判断是否需要更新
	private boolean checkUpdate(){
		String url = HttpUtil.BASE_URL+"android_checkUpdate.action";
		
		try {
			PackageInfo info = this.getPackageManager().getPackageInfo(this.getPackageName(), 0);
			int oldVersion = info.versionCode;
			System.out.println(oldVersion);
			String flat = HttpUtil.queryStringForPost(url);
			int newVersion = Integer.parseInt(flat);
			System.out.println(oldVersion+"============"+newVersion);
			if(newVersion > oldVersion){
				return true;
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return false;
	}

	
	private void down(final String url){
		 new Thread() {  
	            public void run() {  
	                HttpClient client = new DefaultHttpClient();  
	                HttpGet get = new HttpGet(url);  
	                HttpResponse response;  
	                try {  
	                    response = client.execute(get);  
	                    HttpEntity entity = response.getEntity();  
	                    long length = entity.getContentLength();  
	                    InputStream is = entity.getContent();  
	                    FileOutputStream fileOutputStream = null;  
	                    if (is != null) {  
	  
	                        File file = new File(Environment  
	                                .getExternalStorageDirectory(), "cpjw.apk");  
	                        fileOutputStream = new FileOutputStream(file);  
	                          
	                        byte[] buf = new byte[1024];  
	                        int ch = -1;  
	                        while ((ch = is.read(buf)) != -1) {  
	                            fileOutputStream.write(buf, 0, ch);  
	                        }  
	  
	                    }  
	                    fileOutputStream.flush();  
	                    if (fileOutputStream != null) {  
	                        fileOutputStream.close();  
	                    }  
	                    update();
	                    
	                } catch (ClientProtocolException e) {  
	                    // TODO Auto-generated catch block  
	                    e.printStackTrace();  
	                } catch (IOException e) {  
	                    // TODO Auto-generated catch block  
	                    e.printStackTrace();  
	                }  
	            }  
	  
	        }.start(); 
	}
	
	private void update() {    
		mProgressHandler.post(new Runnable() {    
            public void run() {    
            	pd.cancel();
            	install();    
            	finish();
            }    
        });    
    
    }    
    
   private void install() {    
	   	Intent i = new Intent();
	   	//i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
       	i.setAction(Intent.ACTION_VIEW);    
        i.setDataAndType(Uri.fromFile(new File("/sdcard/cpjw.apk")),    
                "application/vnd.android.package-archive");    
        startActivity(i);  
        //finish();
    } 
   
   @Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {

		// 按下键盘上返回按钮
		if (keyCode == KeyEvent.KEYCODE_BACK) {

			new AlertDialog.Builder(this)
					.setMessage("确定退出系统吗?")
					.setNegativeButton("取消",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int which) {
								}
							})
					.setPositiveButton("确定",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int whichButton) {
									finish();
								}
							}).show();

			return true;
		} else {
			return super.onKeyDown(keyCode, event);
		}
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		// 或者下面这种方式
		//System.exit(0);
		//建议用这种
		android.os.Process.killProcess(android.os.Process.myPid());
	}
}

首先 这个Activity 是在我们的应用启动时 就转到这个Activity里面 判断 更新 如果没有更新 就转到业务应用界面

1、判断是否需要更新

主要是根据版本号 来控制 在清单文件里面

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.huitu.project"
      android:versionCode="1"
      android:versionName="1.0">

我们在服务器建一张表 表里可以只有一条数据 这个数据就是最新的版本号

我们获得这个版本号  如果大于手机当前版本号 就提示更新

2、下载最新apk

我们在服务器 可以写一个文件上传的页面 把文件上传到一个固定的目录下面 上传成功后 还要更新数据库最新版本号

3、更新apk

i.setAction(Intent.ACTION_VIEW);    
        i.setDataAndType(Uri.fromFile(new File("/sdcard/cpjw.apk")),    
                "application/vnd.android.package-archive");    
        startActivity(i);  

这样 就会开始安装apk

但是 这里有个问题 总是 提示“应用程序未安装

下载时成功了 但是就是安装不上

我在手机上 也是安装不上 只能把原来的应用卸载 然后安装才会成功

后来 我就想 是不是可以在程序里面先卸载 再安装啊  于是:

//   private void uninstall(){
//	   Intent intent = new Intent(Intent.ACTION_DELETE);    
//	   Uri data = Uri.parse("package:"+this.getPackageName());
//	   intent.setData(data);
//	   startActivity(intent);
//   }
//   
//   private class UninstallReceiver extends BroadcastReceiver{
//
//	   @Override
//	   public void onReceive(Context context, Intent intent) {
//		   System.out.println("rrrrrrrrrrrrrrr");
//		   Toast.makeText(context, "1345435", 1).show();
//		   install();
//	   }
//	   
//   }
//   
//
//   @Override
//   protected void onResume() {
//	   IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_DATA_CLEARED);  
//       filter.addAction(Intent.ACTION_PACKAGE_REMOVED);  
//       filter.addDataScheme("package");  
//       ur = new UninstallReceiver();  
//       registerReceiver(ur, filter);  
//       super.onResume();  
//   }

	@Override
	protected void onDestroy() {
		super.onDestroy();
		// 或者下面这种方式
		//System.exit(0);
		//建议用这种
		//取消注册
		unregisterReceiver(ur);
		android.os.Process.killProcess(android.os.Process.myPid());
	}

但是 这个是不行的 应该只能监听别的应用卸载 监听不到自身吧

卸载后 应用就直接退出了 根本不会执行 下载和安装了

这个也是一个问题 希望有人能解决 。。。。

android上的http

android上面有两种通讯代码:

  第一种是用java.net和java.io包来搞

  第二种用apache的开源项目,而android就集成了apache的开源项目,所以推荐使用这个

先说说基于apache的开源项目的:

     

     HttpPost post = new HttpPost(urlStr);

//有一个httpPost对象,这个对象可以绑定参数——setEntity()

比如:可以绑定一个json对象的string

JSONObject holder = new JSONObject();

holder.put(“version”, “1.1.0”);

holder.put(“host”, “maps.google.com”);

StringEntity se = new StringEntity(holder.toString());

或者是一个list

List <NameValuePair> nvps = new ArrayList <NameValuePair>();

nvps.add(new BasicNameValuePair(“s_username”, strUID));

nvps.add(new BasicNameValuePair(“s_password”, strUPW));    

httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

把要发送的信息准备好后,可以发送了,这要用到发送的客户端

DefaultHttpClient client = new DefaultHttpClient();

执行发送命令并返回结果

HttpResponse resp = client.execute(post);

得到实例:

HttpEntity entity = resp.getEntity();

获得inputStream

InputStream is = httpEntity.getContent();

Bitmap bitmap = BitmapFactory.decodeStream(is); 

is.close(); 

iv.setImageBitmap(bitmap); 

BufferedReader br =

new BufferedReader(new InputStreamReader(entity.getContent()));

循环拿出string

或者:最好是用apache 的 s =  EntityUtils.toString(entity);

然后说:基于原始的java.io包—-

   URL url =new URL(urlStr);    HttpURLConnection con=(HttpURLConnection)url.openConnection();

     /* 允许Input、Output,不使用Cache */

      con.setDoInput(true);

      con.setDoOutput(true);

      con.setUseCaches(false);

      /* 设置传送的method=POST */

      con.setRequestMethod(“POST”);

      /* setRequestProperty */ 

      con.setRequestProperty(“Connection”, “Keep-Alive”);

      con.setRequestProperty(“Charset”, “UTF-8”);

      con.setRequestProperty(“Content-Type”,

                         “multipart/form-data;boundary=****”);

      con.setRequestProperty(“type”, “txt”);

      con.setRequestProperty(“name”, “e”);

   //相当于设置了一个HttpPost 

   

DataOutputStream ds =

        new DataOutputStream(con.getOutputStream()); //准备输出数据

/* 取得文件的FileInputStream */

      FileInputStream fStream = new FileInputStream(uploadFile);

    

      /* 设置每次写入1024bytes */

      int bufferSize = 1024;

      byte[] buffer = new byte[bufferSize];

      int length = -1;

      /* 从文件读取数据至缓冲区 */

      while((length = fStream.read(buffer)) != -1)

      {

        /* 将资料写入DataOutputStream中 */

        ds.write(buffer, 0, length); //在输出流里面写数据

      }

      /* close streams */

      fStream.close();

      ds.flush();

   

AndroidAsyncTask

Android的AsyncTask比Handler更轻量级一些,适用于简单的异步处理。

推荐三篇:Android开发中AsyncTask实现异步处理任务的方法,Android中AsyncTask的用法实例,Android AsyncTask的使用。

首先明确Android之所以有Handler和AsyncTask,都是为了不阻塞主线程(UI线程),且UI的更新只能在主线程中完成,因此异步处理是不可避免的。

Android 1.5提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单。不需要借助线程和Handler即可实现。

AsyncTask 的优势体现在:

 线程的开销较大,如果每个任务都要创建一个线程,那么应用程 序的效率要低很多;

 线程无法管理,匿名线程创建并启动后就不受程序的控制了,如果有很多个请求发送,那么就会启动非常多的线程,系统将不堪重负。

 另外,前面已经看到,在新线程中更新UI还必须要引入handler,这让代码看上去非常臃肿。

AsyncTask定义了三种泛型类型 Params,Progress和Result。

     Params 启动任务执行的输入参数,比如HTTP请求的URL。

     Progress 后台任务执行的百分比。

     Result 后台执行任务最终返回的结果,比如String。

AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,开发者需要实现一个或几个方法。在任务的执行过程中,这些方法被自动调用。

onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。

doInBackground(Params…), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。

onProgressUpdate(Progress…),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。

onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.

使用AsyncTask类,以下是几条必须遵守的准则:

    1) Task的实例必须在UI thread中创建

    2) execute方法必须在UI thread中调用

    3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法

    4) 该task只能被执行一次,否则多次调用时将会出现异常

一个简单进度条的例子:

< xml version=”1.0″ encoding=”UTF-8″ >

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”

  android:orientation=”vertical” android:layout_width=”fill_parent”

  android:layout_height=”fill_parent”>

  <ProgressBar android:id=”@+id/progress_bar”

    android:layout_width=”200dip” android:layout_height=”10dip”

     android:layout_gravity=”center”

    android:max=”100″ style=” android:attr/progressBarStyleHorizontal”

    android:progress=”0″>

  </ProgressBar>

</LinearLayout>

import android.app.Activity;

import android.os.AsyncTask;

import android.os.Bundle;

import android.widget.ProgressBar;

public class Double extends Activity {

  public ProgressBar pBar;

  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    pBar = (ProgressBar) findViewById(R.id.progress_bar);

    //AsyncTask.execute()要在主线程里调用

    new AsyncLoader().execute((Void)null);

  }

  public void initProgress() {

    pBar.setProgress(0);

    try {

      Thread.sleep(1000);

    } catch (InterruptedException e) {

      e.printStackTrace();

    }

    pBar.setProgress(50);

    try {

      Thread.sleep(1000);

    } catch (InterruptedException e) {

      e.printStackTrace();

    }

    pBar.setProgress(100); 

  }

   

  //AsyncTask

  class AsyncLoader extends AsyncTask<Void, Void, Integer>{

    @Override

    protected Integer doInBackground(Void… params) {

      initProgress();

      return null;

    }

  }

}

获取网页的例子:

import java.io.ByteArrayOutputStream;

import java.io.InputStream;

import org.apache.http.HttpEntity;

import org.apache.http.HttpResponse;

import org.apache.http.client.HttpClient;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.DefaultHttpClient;

import android.os.AsyncTask;

//设置三种类型参数分别为String,Integer,String    

class PageTask extends AsyncTask<String, Integer, String> {    

        // 可变长的输入参数,与AsyncTask.exucute()对应    

        @Override    

        protected String doInBackground(String… params) {    

                try {    

                        HttpClient client = new DefaultHttpClient();    

                        // params[0] 代表连接的url    

                        HttpGet get = new HttpGet(params[0]);    

                        HttpResponse response = client.execute(get);    

                        HttpEntity entity = response.getEntity();    

                        long length = entity.getContentLength();    

                        InputStream is = entity.getContent();    

                        String s = null;    

                        if (is != null) {    

                                ByteArrayOutputStream baos = new ByteArrayOutputStream();    

                                byte[] buf = new byte[128];    

                                int ch = -1;    

                                int count = 0;    

                                while ((ch = is.read(buf)) != -1) {    

                                        baos.write(buf, 0, ch);    

                                        count += ch;    

                                        if (length > 0) {    

                                                // 如果知道响应的长度,调用publishProgress()更新进度    

                                                publishProgress((int) ((count / (float) length) * 100));    

                                        }    

                                        // 为了在模拟器中清楚地看到进度,让线程休眠100ms    

                                        Thread.sleep(100);    

                                }    

                                s = new String(baos.toByteArray());                        }    

                        // 返回结果    

                        return s;    

                } catch (Exception e) {    

                        e.printStackTrace();    

                }    

                return null;    

        }    

        @Override    

        protected void onCancelled() {    

                super.onCancelled();    

        }    

        @Override    

        protected void onPostExecute(String result) {    

                // 返回HTML页面的内容    

                message.setText(result);    

        }    

        @Override    

        protected void onPreExecute() {    

                // 任务启动,可以在这里显示一个对话框,这里简单处理    

                message.setText(R.string.task_started);    

        }    

        @Override    

        protected void onProgressUpdate(Integer… values) {    

                // 更新进度    

                message.setText(values[0]);    

        }    

}

本文出自 “李晨专栏” 博客,请务必保留此出处http://lichen.blog.51cto.com/697816/486868