i3geek.com
闫庚哲的个人博客

Android 简单示例TCP和UDP传输

yan阅读(1384)

TCP服务器端代码

try {
		Boolean endFlag = false;
		ServerSocket ss = new ServerSocket(12345);
		while (!endFlag) 
		{
			// 等待客户端连接                  
			Socket s = ss.accept();                  
			BufferedReader input = new BufferedReader(newInputStreamReader(s.getInputStream()));
			//注意第二个参数据为true将会自动flush,否则需要需要手动操作output.flush()                  
			PrintWriter output = newPrintWriter(s.getOutputStream(),true);                  
			String message = input.readLine();                  
			Log.d("Tcp Demo", "message from Client:"+message);                  
			output.println("message received!");                  
			//output.flush();                  
			if("shutDown".equals(message))
			{
				endFlag=true;                  
			}                  
			s.close();              
		}              
		ss.close();             
	} catch (UnknownHostException e) {             
		e.printStackTrace();          
	} 
	catch (IOException e) {
		e.printStackTrace();          
	}

TCP客户端代码:

try {
		Socket s = new Socket("localhost", 12345);
		// outgoing stream redirect to socket
		OutputStream out = s.getOutputStream();
		// 注意第二个参数据为true将会自动flush,否则需要需要手动操作out.flush()
		PrintWriter output = new PrintWriter(out, true);
		output.println("Hello IdeasAndroid!");
		BufferedReader input = new BufferedReader(newInputStreamReader(s.getInputStream()));
		// read line(s)
		String message = input.readLine();
		Log.d("Tcp Demo", "message From Server:" + message);
		s.close();
	} 
	catch (UnknownHostException e) 
	{              
		e.printStackTrace();          
	} 
	catch (IOException e) 
	{              
		e.printStackTrace();          
	}

UDP服务器端代码:

// UDP服务器监听的端口         
	Integer port = 12345;
	// 接收的字节大小,客户端发送的数据不能超过这个大小       
	byte[] message = new byte[1024];
	try {              
		// 建立Socket连接              
		DatagramSocket datagramSocket = new DatagramSocket(port);              
		DatagramPacket datagramPacket = new DatagramPacket(message,message.length);
		try {                  
			while (true) {
				// 准备接收数据                      
				datagramSocket.receive(datagramPacket);
				Log.d("UDP Demo", datagramPacket.getAddress().getHostAddress().toString() + ":" + new String(datagramPacket.getData()));
				}              
			} catch (IOException e) {
				e.printStackTrace();              
			}          
		} catch (SocketException e) { 
			e.printStackTrace();         
		}

UDP客户端代码:

public static void send(String message) {
		message = (message == null ? "Hello IdeasAndroid!" : message);
		int server_port = 12345;
		DatagramSocket s = null;
		try {             
			s = new DatagramSocket();          
		} catch (SocketException e) {
			e.printStackTrace();          
		}          
		InetAddress local = null;
		try {
			// 换成服务器端IP             
			local = InetAddress.getByName("localhost");          
		} catch (UnknownHostException e) {
			e.printStackTrace();          
		}          
		int msg_length = message.length();
		byte[] messagemessageByte = message.getBytes();
		DatagramPacket p = new DatagramPacket(messageByte, msg_length, local,server_port);
		try {              
			s.send(p);          
		} catch (IOException e) {
			e.printStackTrace();         
		}      
	}

SQLite数据库Android的封装应用

yan阅读(1689)

注:本文主要是针对SQLite数据库在Android中的封装使用做以记录,具体函数功能暂不详细介绍。

一、简介

SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源世界著名的数据库管理系统来讲,它的处理速度比他们都快。Android系统中也不例外,也是采用SQLite,本节中就学习下在andorid中怎样使用该数据库来存放数据,并且对SQLite完成简单的新建,更新,查询,删除等操作。

二、封装后的类

1.实体类:Person.java
2.抽象类:SQLOperate.java(封装了对数据库的操作)
3.助手类:DBOpenHelper.java(继承SQLiteOpenHelper)
4.实现类:SQLOperateImpl.java(实现抽象类SQLOperate.java)
5.测试类:Test.java(继承AndroidTestCase)

三、具体实现

1.Person.java

package com.mrzhu.sqltite;  

public class Person {  

    private int _id;  
    private String name;  

    public int getId() {  
        return _id;  
    }  

    public void setId(int _id) {  
        this._id = _id;  
    }  

    public String getName() {  
        return name;  
    }  

    public void setName(String name) {  
        this.name = name;  
    }  

    @Override  
    public String toString() {  
        return "Person [id=" + _id + ", name=" + name + "]";  
    }  

    public Person() {  
        super();  
    }  

    public Person(int _id, String name) {  
        super();  
        this._id = _id;  
        this.name = name;  
    }  
}

2.SQLOperate.java

package com.mrzhu.sqltite;  

import java.util.List;  

/**  
 * 增删改查  
 * @author YGZ  
 *  
 */  
public interface SQLOperate {  
    public void add(Person p);  
    public void delete(int id);  
    public void updata(Person p);  
    public List<Person> find();  
    public Person findById(int id);  
}

3.DBOpenHelper.java

package com.mrzhu.sqltite;  

import android.content.Context;  
import android.database.sqlite.SQLiteDatabase;  
import android.database.sqlite.SQLiteOpenHelper;  

/**  
 * 助手类  
 * @author YGZ  
 *  
 */  
public class DBOpneHelper extends SQLiteOpenHelper {  

    private static final int VERSION = 1;//版本  
    private static final String DB_NAME = "people.db";//数据库名  
    public static final String STUDENT_TABLE = "student";//表名  
    public static final String _ID = "_id";//表中的列名  
    public static final String NAME = "name";//表中的列名  
    //创建数据库语句,STUDENT_TABLE,_ID ,NAME的前后都要加空格  
    private static final String CREATE_TABLE = "create table " + STUDENT_TABLE + " ( " + _ID + " Integer primary key autoincrement," + NAME + " text)";  

    public DBOpneHelper(Context context) {  
        super(context, DB_NAME, null, VERSION);  
    }  

    //数据库第一次被创建时调用   
    @Override  
    public void onCreate(SQLiteDatabase db) {  
        db.execSQL(CREATE_TABLE);  
    }  

    //版本升级时被调用   
    @Override  
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  

    }  

}

4.SQLOperateImpl.java

package com.mrzhu.sqltite;  

import java.util.ArrayList;  
import java.util.List;  

import android.content.ContentValues;  
import android.content.Context;  
import android.database.Cursor;  
import android.database.sqlite.SQLiteDatabase;  

public class SQLOperateImpl implements SQLOperate{  

    private DBOpneHelper dbOpenHelper;  

    public SQLOperateImpl(Context context) {  
        dbOpenHelper = new DBOpneHelper(context);  
    }  

    /**  
     * 增,用insert向数据库中插入数据  
     */  
    public void add(Person p) {  
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();  
        ContentValues values = new ContentValues();  
        values.put(DBOpneHelper._ID, p.getId());  
        values.put(DBOpneHelper.NAME, p.getName());  
        db.insert(DBOpneHelper.STUDENT_TABLE, null, values);  
    }  

    /**  
     * 删,通过id删除数据  
     */  
    public void delete(int id) {  
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();  
        db.delete(DBOpneHelper.STUDENT_TABLE, DBOpneHelper._ID + "=?", new String[]{String.valueOf(id)});  
    }  

    /**  
     * 改,修改指定id的数据  
     */  
    public void updata(Person p) {  
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();  
        ContentValues values = new ContentValues();  
        values.put(DBOpneHelper._ID, p.getId());  
        values.put(DBOpneHelper.NAME, p.getName());  
        db.update(DBOpneHelper.STUDENT_TABLE, values, DBOpneHelper._ID + "=?", new String[]{String.valueOf(p.getId())});  
    }  

    /**  
     * 查,查询表中所有的数据  
     */  
    public List<Person> find() {  
        List<Person> persons = null;  
        SQLiteDatabase db = dbOpenHelper.getReadableDatabase();  
        Cursor cursor = db.query(DBOpneHelper.STUDENT_TABLE, null, null, null, null, null, null);  
        if(cursor != null){  
            persons = new ArrayList<Person>();  
            while(cursor.moveToNext()){  
                Person person = new Person();  
                int _id = cursor.getInt(cursor.getColumnIndex(DBOpneHelper._ID));  
                String name = cursor.getString(cursor.getColumnIndex(DBOpneHelper.NAME));  
                person.setId(_id);  
                person.setName(name);  
                persons.add(person);  
            }  
        }  
        return persons;  
    }  

    /**  
     * 查询指定id的数据  
     */  
    public Person findById(int id) {  
        SQLiteDatabase db = dbOpenHelper.getReadableDatabase();  
        Cursor cursor = db.query(DBOpneHelper.STUDENT_TABLE, null, DBOpneHelper._ID + "=?", new String[]{String.valueOf(id)}, null, null, null);  
        Person person = null;  
        if(cursor != null && cursor.moveToFirst()){  
            person = new Person();  
            int _id = cursor.getInt(cursor.getColumnIndex(DBOpneHelper._ID));  
            String name = cursor.getString(cursor.getColumnIndex(DBOpneHelper.NAME));  
            person.setId(_id);  
            person.setName(name);  
        }  
        return person;  
    }  
}

5.Test.java

package com.mrzhu.sqltite;  

import java.util.List;  

import android.test.AndroidTestCase;  
import android.util.Log;  

public class Test extends AndroidTestCase {  
    public void testAdd() throws Exception{  
        SQLOperateImpl test = new SQLOperateImpl(getContext());  
        Person person = new Person(2, "Peter");  
        test.add(person);  
    }  

    public void testDelete() throws Exception{  
        SQLOperateImpl test = new SQLOperateImpl(getContext());  
        test.delete(1);  
    }  

    public void testUpdata() throws Exception{  
        SQLOperateImpl test = new SQLOperateImpl(getContext());  
        Person person = new Person(1, "Tom");  
        test.updata(person);  
    }

    public void testFind() throws Exception{  
        SQLOperateImpl test = new SQLOperateImpl(getContext());  
        List<Person> persons = test.find();  
        for (Person person : persons) {  
            Log.i("System.out", person.toString());  
        }  
    }  

    public void testFindById() throws Exception{  
        SQLOperateImpl test = new SQLOperateImpl(getContext());  
        Person person = test.findById(2);  
        Log.i("System.out", person.toString());  
    }  
}

 

Android报表库aChartEngine系列(介绍)

yan阅读(4406)

1、前言 

今天准备为大家讲讲aChatEngine方面的知识,在之后的一段时间里会陆续讲aChartEngine如何使用的Demo。

为什么选择aChartEngine给大家讲呢?因为在android平台上难免会遇到一些图表的处理,aChartEngine是个易学易用的图表库并且功能强大。

2、AchartEngine是什么?

总的来说,aChartEngine是一个android应用的图表库,他支持一些常见的一些图表(文档中说明的那些类型)。下面是官方文档:

AChartEngine is a charting library for Android applications. It currently supports the following chart types:

· line chart

· area chart

· scatter chart

· time chart

· bar chart

· pie chart

· bubble chart

· doughnut chart

· range (high-low) bar chart

· dial chart / gauge

· combined (any combination of line, cubic line, scatter, bar, range bar, bubble) chart

· cubic line chart

All the above supported chart types can contain multiple series, can be displayed with the X axis horizontally (default) or vertically and support many other custom features. The charts can be built as a view that can be added to a view group or as an intent, such as it can be used to start an activity.

The model and the graphing code is well optimized such as it can handle and display huge number of values.

3、AChartEngine能做什么?

它是一个图表库,所以它的功能和图表是分不开的,它主要功能是用图表的方式来显示数据,适用于数据分析,报表显示,当然图表能表达的其他功能它都应该有。下面是常见的图表效果:

396_1 396_2 396_3 396_4 396_5

 备注:aChartEngine能做的图表不仅限于上面这几个效果,这几个主要是比较常用而已。

4、使用aChartEngine前的准备

AchartEngine的项目地址:http://code.google.com/p/achartengine/ 。在你使用aChartEngine之前首先你应该下载几个文件,如图:

下载地址http://code.google.com/p/achartengine/downloads/list。

demo_source.zip里面当然是一些demo,后面我整理的demo也是根据这个demo包修改而成的;

javadoc.zip当然是一些文档;

achartengine-1.0.0.jar这个jar也就是你开发需要的jar包。

 5、建立aChartEngine项目

 建立项目和普通的android应用是一样的,建立好项目以后你只需要引入achartengine-1.0.0.jar这个包就可以了,这个项目就可以成功的使用achartengine库提供的所有功能了。

6、总结

这里只是对aChartEngine进行了一个概括,你可以认真阅读官方的demo自己摸索它是如何使用的,在后面我也会整理一些比较简单的demo(只演示如何使用)。

Android 线程菜鸟应用,浅析post和sendMessage

yan阅读(4088)

辛辛苦苦写的程序,在安卓4.0上运行不了,苦了我了。查了半天发现是4.0的主线程上不能执行联网操作,只得单启线程运行。同时线程内不得更新UI,handler监听改UI。

一、线程的应用

简单的启线程

new Thread(new Runnable() {
				public void run() {
				//要执行的代码
				     }
			}).start();

带Handler.sendEmptyMessage修改UI的线程

// 定义Handler对象  
final Handler handler = new Handler() {  
    @Override  
    // 当有消息发送出来的时候就执行Handler的这个方法  
    public void handleMessage(Message msg) {  
        super.handleMessage(msg);  
        // 处理UI  
		switch(msg.what){
		case 0:
		//要处理的事情
			break;
		}
    }  
};  
new Thread() {  
    @Override  
    public void run() {  
        // 你要执行的方法  
        // 执行完毕后给handler发送一个空消息  
        handler.sendEmptyMessage(0);  
    }  
}.start();

带Handler.post的修改UI的线程

new Thread(new Runnable()
{
	@Override
	public void run()
	{
		//要执行的内容

		handler.post(new Runnable()
		{
			public void run()
			{
			//要执行更新界面的内容
			}
		});
	}
}).start();

 二、post和sendMessage

先看一下Message类中定义一个私有的变量:Runnable callback;

再来看一下handler.post(Runnable callback)方法的源码:

public final boolean post(Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
}

再看一下sendMessageDelayed的源码:

 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

这里面有个关键就是方法getPostMessage(r)这个方法,他将Runnable转成一个Message,他内部到底干了什么呢?看一下他的源码:

private final Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

这里面就是将Runnable转化成一个Message,其他看他的代码很简单,就是先获取一个空消息Message.obtain(),然后将Message中的callback的值设置成Runnable,这时候就了解到了Message中的callback的作用了!

同时也了解一下View.post(Runnable r)方法的作用:看一下实例代码:

final Button btn = (Button)findViewById(R.id.btn);
btn.post(new Runnable(){
@Override
public void run() {
btn.setText("不是好人");
}
});    
}

上面的代码就是更新btn中的内容,同样下面的代码也可以达到这种效果:

Handler handler = new Handler();
final Button btn = (Button)findViewById(R.id.btn);
handler.post(new Runnable(){
@Override
public void run() {
btn.setText("不是好人");
}
});    
}

不同是这个是用handler.post方法,一个是用View.post方法,现在来看一下View.post方法的源代码:

 public boolean post(Runnable action) {
        Handler handler;
        AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            handler = attachInfo.mHandler;
        } else {
            // Assume that post will succeed later
            ViewRootImpl.getRunQueue().post(action);
            return true;
        }
        return handler.post(action);
    }

方法中主要的功能代码就是attachInfo.mHandler,获取当前线程的hanlder,和我们在一个线程中定义一个Handler的效果是一样的。

Android 【解答】关于报错You cannot combine custom titles with other title features

yan阅读(2614)

对于这个问题,,产生的原因不知一个,,,,看看你符合那一条:::

one: 在tab中出现的错误

解决办法:http://hi.baidu.com/notenking/item/0fa8cfd84f09e44bdcf9becb

two:4.0以上title冲突问题

解决办法:http://hi.baidu.com/xdyang1986/item/ea5a991c75de477b7b5f25f0

上面的办法能行但是,,,,窗口界面就成小的了;

下面有更好地办法:http://www.apkbus.com/android-80415-1-1.html

这个办法很给力,,解决是解决了,但是又出现了新的问题(针对特殊情况,AndroidManifest.xml 中,,将android:theme=”@style/AppTheme” 不能不换成android:theme=”@style/android:Theme.Light”,因为我的appTheme里面有其他东西)

怎么办:请看此文http://mobile.51cto.com/aprogram-387074.htm看完后与上篇的文章对比总结一下

原来在文件夹[values-v11和values-v14中的styles.xml]两个文件中的parent继承的都不是android:Theme,所以把他们都改成android:Theme,然后再把values文件加下的styles中对应的主题下的<item>统统拷贝到这两文件加下的styles  中,,,运行就ok了

Android ProgressDialog多线程进度框的实现

yan阅读(3287)

1、声明变量

    private Handler handler = new Handler();
    private ProgressDialog progressDialog = null;
    private int result;

 2、在开始进行网络连接时显示进度条对话框

  progressDialog = ProgressDialog.show(MyActivity.this, "请稍等...", "获取数据中...", true);

 3、添加异步操作

	new Thread(new Runnable(){

    @Override
        public void run() {
        //加载数据
            result=0;//初始化状态
            try{
				//要耗时运行的程序
				dosomething();
                result=1;//标记成功运行
            }
            catch(Exception ex){
                result=-1; //标记失败
            }           
         //更新界面
            handler.post(new Runnable() {     
                public void run() {                          
                    if(result==1)
					{
					//耗时程序完成后,进行更新界面
						init();
					}
                    else
                        Toast.makeText(m_context, "连接失败,请检查网络连接", Toast.LENGTH_SHORT).show();    
                }                  
            });
            progressDialog.dismiss();
        }
	}).start();

 示例:

//按钮监听事件
bt_subButton2.setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View v)
			{
				// TODO Auto-generated method stub
				progressDialog = ProgressDialog.show(TianqiMain.this, "请稍等...", "获取数据中...", true);

				new Thread(new Runnable(){
                    @Override
                    public void run() {
                   //加载数据
                         result=0;
                          try{
                              //要耗时运行的程序
							  dosomething();

                             result=1;
                          }
                         catch(Exception ex){
                             result=-1; 
                         }           

                    //更新界面
                         // Update the progress bar 
                         handler.post(new Runnable() {     
                             public void run() {                          
                                 if(result==1)
                                 {
                                	//耗时程序完成后,进行更新界面
									init();
                                 }
                                       else
                                           Toast.makeText(getApplication(), "下载文件失败,请检查网络连接", Toast.LENGTH_SHORT).show();    
                                 }                
                             });
                         progressDialog.dismiss();
                    }}).start();
			}
		});

 

Android 自定义标题栏TitleBar

yan阅读(2782)

很多网友发现自己Android程序的标题栏TitleBar区域很单调,如果想个性化一些可以通过下面的方法来为自己软件的标题
定制一个layout布局文件,比如浏览器的标题栏,它包含了网站的Favicon,自定义的进度条,和不确定的进度指示等等,
实现的方法自己控制吧。下面代码在onCreate中使用,同时顺序不要改变,否则将无法生效:
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);   
setContentView(R.layout.main);   //软件activity的布局
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.titlebar);  //titlebar为自己标题栏的布局

Layout下建立titlebar.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical"
    android:orientation="horizontal" >

    <Button
        android:id="@+id/back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/back"
        android:textSize="18sp"
        android:textColor="#FF0000FF" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/share"
        android:textSize="18sp"
        android:textColor="#FFFFFF00" />
</LinearLayout>

 

这样虽然可以在一定程度上定制标题栏, 不过, 这里无法改变标题栏的高度和背景(背景设置之后会在两端有两个
非常难看的边框).  据说, 原因是android 固有的.
这里有修改方法:
  原理是这样的. 直接像上述代码那样添加title仅仅是把一个子界面添加到原有的title上的, 并没有改变原来的属性,
比如 标题栏大小, 标题栏背景. 这些需要在theme 主题里面定义.
  因此先定义一个style, 若修改背景请修改android:windowTitleBackgroundStyle
  若修改标题栏高度,请修改android:windowTitleSize
例子:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">

<style name="CustomWindowTitleBackground">
       <item name="android:background">#565656</item>
</style>

<style name="test" parent="android:Theme">
     <item name="android:windowTitleSize">50dp</item>
     <item name="android:windowTitleBackgroundStyle">@style/CustomWindowTitleBackground</item>
</style>
</resources>

在程序的android manifest.xml中对应:

  activity中添加属性  android:theme = “@style/test”  就可以了
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.guardian"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name" >
        <activity android:name=".Guardian"
                  android:label="@string/app_name"
                  android:theme = "@style/test"   //就在这里
                  >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
    <uses-sdk android:minSdkVersion="4" />

</manifest>

之后借助于设置自定义的标题栏xml文件,就可以自定义标题栏布局了

Android 在ImageView 上画个正方形

yan阅读(3872)

public void first()
{
  // 防止出现Immutable bitmap passed to Canvas constructor错误
  Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.c0101a).copy(Bitmap.Config.ARGB_8888, true);
  //Bitmap bitmap2 = ((BitmapDrawable) getResources().getDrawable(R.drawable.go)).getBitmap();
  Bitmap newBitmap = null;
  newBitmap = Bitmap.createBitmap(bitmap1);
  Canvas canvas = new Canvas(newBitmap);
  Paint paint = new Paint();
  int w = bitmap1.getWidth();
  int h = bitmap1.getHeight();
//  int w_2 = bitmap2.getWidth();
//  int h_2 = bitmap2.getHeight();

  paint.setAntiAlias(true);//设置画笔无锯齿(如果不设置可以看到效果很差)
  /*设置paint的 style 为STROKE:空心*/
        paint.setStyle(Paint.Style.STROKE);
  paint.setColor(Color.RED);
  paint.setAlpha(125);
  canvas.drawRect(100, 100, 50, 50, paint);
  //paint = new Paint();
  //canvas.drawBitmap(bitmap2, Math.abs(w - w_2) / 2, Math.abs(h - h_2) / 2, paint);
  canvas.save(Canvas.ALL_SAVE_FLAG);
  // 存储新合成的图片
  canvas.restore();
  find_img1.setImageBitmap(newBitmap);
}

 

Android 相机、相册获取图片地址、显示并保存到SD卡

yan阅读(3789)

如题,这个需求本不是一个很复杂的过程,但是却存在一些隐患,我也是最近在项目中碰到这个问题,将Android通过相机或相册获取图片并最终显示在界面上做了一个小研究,现将一些结果和附上的一个Demo叙述如下:
做过类似需求的同学都知道,在Activity中通过如下代码可以启动相机,然后在重写的onActivityResult方法中可以获取到返回的照片数据:

Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  
startActivityForResult(openCameraIntent, TAKE_PICTURE);

问题来了,不知大家是否有发现,在onActivityResult方法里通过Intent的getData方法获取的数据转换成bitmap并显示在界面上,有时候会有取不到数据,或者显示的bitmap会非常小,如果将bitmap保存到sd卡后会发现,图片的分辨率很低,并且图片大小也是经过压缩的,不管将相机的像素设置多高,最后通过这种方式返回的bitmap总是经过压缩了的。如果想获得理想的照片大小和分辨率改如何处理呢?以下是我的处理方法,虽然不是最好,但是帮我解决了这个需求。我先来简述一下为什么返回的图片是经过了压缩的。

大家都知道,现在手机像素少则500W或800W,多则4KW(某亚),就拿常见的800W像素的相机拍出来的照片来说,分辨率大概在3200*2400左右,我的测试机型是LG optimus 2x,2.3.7的系统,用800W像素拍出来大概就是这个分辨率,照片大小在2M左右。试想一下,在Android系统中bitmap占用4个字节,3200*2400*4=?,结果大家自己算算,如果为了一张图片,耗用这么大的内存,肯定是不合理的,并且,官方文档中有说明,Android系统分配给每个应用的最大内存是16M,所以,系统为了防止应用内存占用过大,对于在应用内通过相机拍摄的图片最终返回来的结果进行了压缩,压缩后的图片变得很小,通过之前说的getData的方式只能满足比如显示个头像这样的需求,如果要显示大图,就会出现模糊的情况。那如何获取清晰的大图呢?我的解决思路如下:

1.拍照时,将拍得的照片先保存在本地,通过修改之前的代码如下:

Uri imageUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(),"image.jpg"));  
//指定照片保存路径(SD卡),image.jpg为一个临时文件,每次拍照后这个图片都会被替换  
openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

 2.在onActivityResult方法中再将图片取出,并经过缩小处理再显示在界面上或上传给服务器(压缩比例自定义)

@Override  
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
        super.onActivityResult(requestCode, resultCode, data);  
        if (resultCode == RESULT_OK) {  
            switch (requestCode) {  
            case TAKE_PICTURE:  
                //将保存在本地的图片取出并缩小后显示在界面上  
                Bitmap bitmap = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+"/image.jpg");  
                Bitmap newBitmap = ImageTools.zoomBitmap(bitmap, bitmap.getWidth() / SCALE, bitmap.getHeight() / SCALE);  
                //由于Bitmap内存占用较大,这里需要回收内存,否则会报out of memory异常  
                bitmap.recycle();  

                //将处理过的图片显示在界面上,并保存到本地  
                iv_image.setImageBitmap(newBitmap);  
                ImageTools.savePhotoToSDCard(newBitmap, Environment.getExternalStorageDirectory().getAbsolutePath(), String.valueOf(System.currentTimeMillis()));  
                break;  
            default:  
                break;  
            }  
        }  
    }

由于Android给bitmap分配的内存最大不超过8M,所以对使用完的较大的Bitmap要释放内存,调用其recycle()方法即可。然后将缩小(缩小方法在Demo源码中有)后的bitmap显示在界面上或保存到SD卡,至于之前保存的原图,可以删掉,也可以放在那,下次拍照时,这张原图就会被下一张照片覆盖,所以SD卡上使用只有一张临时图片,占用也不是很大。

以上讲的是拍照获取图片,如果是从相册中获取图片又如何处理呢,我的方法如下:

1.打开相册选取图片

Intent openAlbumIntent = new Intent(Intent.ACTION_GET_CONTENT);  
                    openAlbumIntent.setType("image/*");  
                    startActivityForResult(openAlbumIntent, CHOOSE_PICTURE);

 2.在onActivity方法中处理获取到的图片,思路和之前类似

@Override  
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
        super.onActivityResult(requestCode, resultCode, data);  
        if (resultCode == RESULT_OK) {  
            switch (requestCode) {  
            case CHOOSE_PICTURE:  
                ContentResolver resolver = getContentResolver();  
                //照片的原始资源地址  
                Uri originalUri = data.getData();   
                try {  
                    //使用ContentProvider通过URI获取原始图片  
                    Bitmap photo = MediaStore.Images.Media.getBitmap(resolver, originalUri);  
                    if (photo != null) {  
                        //为防止原始图片过大导致内存溢出,这里先缩小原图显示,然后释放原始Bitmap占用的内存  
                        Bitmap smallBitmap = ImageTools.zoomBitmap(photo, photo.getWidth() / SCALE, photo.getHeight() / SCALE);  
                        //释放原始图片占用的内存,防止out of memory异常发生  
                        photo.recycle();  

                        iv_image.setImageBitmap(smallBitmap);  
                    }  
                } catch (FileNotFoundException e) {  
                    e.printStackTrace();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }    
                break;  

            default:  
                break;  
            }  
        }  
    }

 获取图片的路径

String[] proj = {MediaStore.Images.Media.DATA};

            //好像是android多媒体数据库的封装接口,具体的看Android文档
            Cursor cursor = managedQuery(originalUri, proj, null, null, null); 
            //按我个人理解 这个是获得用户选择的图片的索引值
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            //将光标移至开头 ,这个很重要,不小心很容易引起越界
            cursor.moveToFirst();
            //最后根据索引值获取图片路径
            String path = cursor.getString(column_index);
        }catch (IOException e) {
            Log.e(TAG,e.toString()); 
        }

 

Android ListView用法(二)

yan阅读(2850)

ListView是比较常用的控件,但一直都觉得创建ListView步骤有点繁琐,故在此总结一下,方便查阅。

程序效果是实现一个ListView,ListView里面有标题,内容和图片,并加入点击和长按响应。

首先在xml里面定义一个ListView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
	android:id="@+id/LinearLayout01" 
	android:layout_width="fill_parent" 
	android:layout_height="fill_parent" 
	xmlns:android="http://schemas.android.com/apk/res/android">
<ListView android:layout_width="wrap_content" 
          android:layout_height="wrap_content" 
          android:id="@+id/ListView01"
          />
</LinearLayout>

定义ListView每个条目的Layout,用RelativeLayout实现:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
	android:id="@+id/RelativeLayout01" 
	android:layout_width="fill_parent" 
	xmlns:android="http://schemas.android.com/apk/res/android" 
	android:layout_height="wrap_content" 
	android:paddingBottom="4dip" 
	android:paddingLeft="12dip"
	android:paddingRight="12dip">
<ImageView 
	android:paddingTop="12dip"
	android:layout_alignParentRight="true"
	android:layout_width="wrap_content" 
	android:layout_height="wrap_content" 
	android:id="@+id/ItemImage"
	/> 
<TextView 
    android:text="TextView01" 
    android:layout_height="wrap_content" 
    android:textSize="20dip" 
    android:layout_width="fill_parent" 
    android:id="@+id/ItemTitle"
    />
<TextView 
	android:text="TextView02" 
	android:layout_height="wrap_content" 
	android:layout_width="fill_parent" 
	android:layout_below="@+id/ItemTitle" 
	android:id="@+id/ItemText"
	/>
</RelativeLayout>

最后在Activity里面调用和加入Listener,具体见注释:

package com.ray.test;

import java.util.ArrayList;
import java.util.HashMap;

import android.app.Activity;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.AdapterView.OnItemClickListener;

public class TestListView extends Activity {
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        //绑定Layout里面的ListView
        ListView list = (ListView) findViewById(R.id.ListView01);

        //生成动态数组,加入数据
        ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>();
        for(int i=0;i<10;i++)
        {
        	HashMap<String, Object> map = new HashMap<String, Object>();
        	map.put("ItemImage", R.drawable.checked);//图像资源的ID
        	map.put("ItemTitle", "Level "+i);
        	map.put("ItemText", "Finished in 1 Min 54 Secs, 70 Moves! ");
        	listItem.add(map);
        }
        //生成适配器的Item和动态数组对应的元素
        SimpleAdapter listItemAdapter = new SimpleAdapter(this,listItem,//数据源 
            R.layout.list_items,//ListItem的XML实现
            //动态数组与ImageItem对应的子项        
            new String[] {"ItemImage","ItemTitle", "ItemText"}, 
            //ImageItem的XML文件里面的一个ImageView,两个TextView ID
            new int[] {R.id.ItemImage,R.id.ItemTitle,R.id.ItemText}
        );

        //添加并且显示
        list.setAdapter(listItemAdapter);

        //添加点击
        list.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
					long arg3) {
				setTitle("点击第"+arg2+"个项目");
			}
		});

      //添加长按点击
        list.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {

			@Override
			public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
				menu.setHeaderTitle("长按菜单-ContextMenu");   
				menu.add(0, 0, 0, "弹出长按菜单0");
				menu.add(0, 1, 0, "弹出长按菜单1");   
			}
		}); 
    }

	//长按菜单响应函数
	@Override
	public boolean onContextItemSelected(MenuItem item) {
		setTitle("点击了长按菜单里面的第"+item.getItemId()+"个项目"); 
		return super.onContextItemSelected(item);
	}
}