当前位置:  编程技术>移动开发
本页文章导读:
    ▪TCP连接的两种简略的数据传输方式        TCP连接的两种简单的数据传输方式TCP适合传输自定义原始的字节流,比如传输一个序列化为字节流后的对象或者结构体,发送方按约定的自定义报文结构发送,接收方按约定的自定.........
    ▪ 二级联动城市取舍        二级联动城市选择很多地方用到多级城市选择,有人偏爱苹果的那种滚动的效果。查了下,android有类似的实现,但是实现起来比较麻烦。综合考虑下,还是使用android现有的组件来实现。 多.........
    ▪ asmack实现端到端的讯息回执(XEP-0184: Message Delivery Receipts)       asmack实现端到端的消息回执(XEP-0184: Message Delivery Receipts)要想保证信息的传输,目前在smack/asmack + openfire架构上,我个人想到有两种实现方式: 1.端到端确保发送(类似短信)。 其实这个就是.........

[1]TCP连接的两种简略的数据传输方式
    来源: 互联网  发布时间: 2014-02-18
TCP连接的两种简单的数据传输方式

TCP适合传输自定义原始的字节流,比如传输一个序列化为字节流后的对象或者结构体,发送方按约定的自定义报文结构发送,接收方按约定的自定义报文解码。一种传输字流节,适合传输结构体和对象,一种传输出字符串。

package com.magcomm.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.CharBuffer;

import org.apache.http.util.EncodingUtils;

import android.util.Log;

public class SocketClient {
	private static final String tags = "com.magcomm.nmc, SocketClient";
	private Socket socket;   
	private final String SERVER_HOST_IP = "192.168.0.15";
	private final String SERVER_HOST_NAME = "mail.magcomm.cn";
	private final int SERVER_HOST_PORT = 58889;
	
    private InputStream mInputStream;                 
	private OutputStream mOutputStream; 
    private PrintWriter mPrintWriter; 
	private BufferedReader reader;
    public SocketClient()  
    {  
  
    }  
      
    public boolean connection()  
    {  
        boolean bRet = false;  
        try  
        {  
            //创建一个客户端连接  
            InetAddress ia = InetAddress.getByName(SERVER_HOST_NAME); 
            socket = new Socket(ia, SERVER_HOST_PORT);  
            //socket.connect(new InetSocketAddress(SERVER_HOST_NAME, SERVER_HOST_PORT),10000);
            
            bRet = true;  
        } catch (Exception e)  
        {  
            // TODO: handle exception  
        	Log.i(tags, "socket open error" + e.getMessage());
        }  
              
        return bRet;  
    }  
      
    //发送消息  
	public boolean sendMsg(String msg) {
		boolean bRet = false;
		if (socket != null) {
			if (socket.isConnected()) {
				//获取这个客户端连接SOCKET的输入输出  
				try {
					mPrintWriter = new PrintWriter(socket.getOutputStream(), true);
					mPrintWriter.print(msg);
					mPrintWriter.flush();
					bRet = true;
				} catch (IOException e) {
					// TODO Auto-generated catch block
					Log.i(tags, "write socket error");
					e.printStackTrace();
				}
					
			}
		}
		return bRet;
	}
	public boolean sendMsg2(byte[] buffer, int offset, int count) {
		boolean bRet = false;
		if (socket != null) {
			if (socket.isConnected()) {
				//获取这个客户端连接SOCKET的输入输出  
				try {
					mOutputStream = socket.getOutputStream();
					mOutputStream.write(buffer, offset, count);
					mOutputStream.flush();
					Log.i("NMCDataUnPack", "socket send success--------");
					bRet = true;
				} catch (IOException e) {
					// TODO Auto-generated catch block
					Log.i(tags, "write socket error");
					e.printStackTrace();
				}
					
			}
		}
		return bRet;
	}
	
    //读取消息  
    public String readMsg()  
    {  
        String msgString = "";  
        try  
        {  
        	mInputStream = socket.getInputStream(); 
            byte buffer[] = new byte[1024];  
            int reCount = mInputStream.read(buffer);  
            msgString = EncodingUtils.getString(buffer, /*"GB2312"*/"US-ASCII"); 
        }catch (Exception e)  
        {  
        	Log.i(tags, "read socket error");
        	e.printStackTrace();
        } 
        return msgString;  
    }  
    public String readMsg2()  
    {  
        String msgString = "";  
   
        	try {
        		char[] buffer = new char[1024];  
				Reader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				reader.read(buffer);
				msgString = buffer.toString(); 
			} catch (IOException e) {
				Log.i(tags, "read socket error");
				e.printStackTrace();
			}
       
        return msgString;  
    }    
      
    public void close()  
    {  
        try  
        {   mInputStream.close();
        	mPrintWriter.close();
            mOutputStream.close();
            socket.close();  
        } catch (Exception e)  
        {  
            // TODO: handle exception  
        	Log.i(tags, "socket close error" + e.getMessage());
        }  
    }  
}



    
[2] 二级联动城市取舍
    来源: 互联网  发布时间: 2014-02-18
二级联动城市选择

很多地方用到多级城市选择,有人偏爱苹果的那种滚动的效果。查了下,android有类似的实现,但是实现起来比较麻烦。综合考虑下,还是使用android现有的组件来实现。

多级联动,一级操作触发其他的改变,很自然的想到用多个listview,多级和二级区别不大,因此暂时实现一个二级的城市选择,即只选择省和市,区县类似。实现的效果如下:



1、数据来源

这里选择网上得来的城市数据:中国天气网的城市数据。具体怎么完美获取最新的,今天尝试了下还是有点麻烦,之后的文章中会补上。当然最简单的方法就是下载别人弄好的。

2、数据库操作

我把数据库直接放到了assets文件夹下面,应用第一次启动的时候会拷贝城市数据库到应用的数据库目录下,然后就可以使用了。然后再封装一个数据库操作类,对其进行简单的操作,代码如下:

public class PubDBM {
	public static final String TABLE_CHINA_CITY_CODE = "china_city_code";
	public static final String CCC_PROVINCE = "province"; //
	public static final String CCC_CITY = "city"; //
	public static final String CCC_COUNTY = "county"; //
	public static final String CCC_CODE = "code"; //

	private static PubDBM dbm;

	private static final String DBNAME = "china_city_code.db";
	private String dbPath;// 数据库路径,不包括数据库名字

	private SQLiteDatabase db = null;
	private static Context mContext;

	public static PubDBM getInstance(Context context) {
		mContext = context;

		if (dbm == null) {
			dbm = new PubDBM();
		}
		return dbm;
	}

	private PubDBM() {
		// TODO context.getFilesDir().getPath()
		dbPath = "/data/data/" + mContext.getPackageName() + "/databases/";

		initPublicDataBase();
	}

	/**
	 * 初始化数据库
	 */
	private void initPublicDataBase() {

		File dbDir = new File(dbPath);
		if (!dbDir.exists()) {
			dbDir.mkdirs();
		}

		File dbFile = new File(dbPath + DBNAME);
		if (!dbFile.exists()) {
			try {
				dbFile.createNewFile();

				InputStream is = mContext.getResources().getAssets().open(DBNAME);
				OutputStream os = new FileOutputStream(dbPath + DBNAME);
				byte[] buffer = new byte[1024];
				int length = 0;
				while ((length = is.read(buffer)) > 0) {
					os.write(buffer, 0, length);
				}
				os.flush();
				os.close();
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
				return;
			}
		}
		if (db == null) {
			db = SQLiteDatabase.openDatabase(dbPath + DBNAME, null, SQLiteDatabase.OPEN_READONLY);
		}
	}

	/**
	 * 关闭数据库
	 */
	public void close() {
		if (db != null && db.isOpen()) {
			db.close();
		}
	}

	/**
	 * 查询所有数据
	 * 
	 * @return
	 */
	public Cursor queryAllData() {
		if (db == null) {
			System.out.println("db==null");
			return null;
		}
		return db.rawQuery("select * from " + TABLE_CHINA_CITY_CODE + " order by " + CCC_CODE + " asc", null);
	}

	/**
	 * 查询省
	 * 
	 * @return
	 */
	public Cursor queryProvinceList() {
		String sql = "select distinct substr(" + CCC_CODE + ",1,5) as _id," + CCC_PROVINCE;
		sql += " from " + TABLE_CHINA_CITY_CODE;
		System.out.println(sql);

		if (db == null) {
			return null;
		}
		return db.rawQuery(sql, null);
	}

	/**
	 * 查询城市
	 * 能力有限,数据库查询不熟悉,结果并不是完全想要的,code对应不上
	 */
	public Cursor queryCityList(String province) {
		String sql = "select * from " + TABLE_CHINA_CITY_CODE;
		sql += " where " + CCC_PROVINCE + " = '" + province + "' group by " + CCC_CITY;
		
		if (db == null) {
			return null;
		}
		return db.rawQuery(sql, null);
	}

	/**
	 * 根据城市代码查询
	 * 
	 */
	public Cursor queryProvinceAndCity(String cityCode) {
		String sql = "select " + CCC_PROVINCE + ", " + CCC_CITY;
		sql += " from " + TABLE_CHINA_CITY_CODE;
		sql += " where " + CCC_CODE + " = " + cityCode;
		if (db == null) {
			return null;
		}
		return db.rawQuery(sql, null);
	}
}
3、界面实现:

在见面上展现就是一个EditText和一个Dialog的组合,点击EditText弹出一个Dialog,选择之后Dialog消失,EditText中显示选择的结果,需要使用的地方通过EditText获取选择的值。由于很多地方带有初始数据,因此我们在界面初始化的时候可能需要显示一个之前选择的城市,这个时候我们可以调用自定义的EditText设置一个初始城市代码,这样就可以显示初始数据了。

/**
 * 城市选择<br>
 * 1、布局中添加此控件<br>
 * 2、拿到此控件,设置数据库对象<br>
 * 3、通过getCityCode()拿到选择的城市代码<br>
 * 4、设置默认值是通过setCityCode(String code)方法<br>
 * 
 * @author ttworking
 * 
 */
public class CitySelect extends EditText implements android.view.View.OnClickListener {
	private String province, city, code;

	private CitySelectDialog dialog;
	private PubDBM dbm;

	public CitySelect(Context context, AttributeSet attrs) {
		super(context, attrs);
		this.dialog = new CitySelectDialog(context);

		this.dbm = PubDBM.getInstance(context);
		this.setOnClickListener(this);
		
		setFocusable(false);
		setClickable(true);
	}

	/**
	 * 获取省份加城市
	 * 
	 * @return 省份 城市
	 */
	public String getProvinceCity() {
		return getText().toString();
	}

	/**
	 * 获取选择的城市代码
	 * 
	 * @return 城市代码
	 */
	public String getCityCode() {
		return code;
	}

	/**
	 * 设置城市信息,参数为城市代码,设置之后界面会显示城市名称
	 * 
	 * @param code
	 *            城市代码
	 */
	public void setCityCode(String code) {
		this.code = code;
		if (dbm == null) {
			return;
		}
		Cursor cursor = dbm.queryProvinceAndCity(code);
		if (cursor != null && cursor.moveToFirst()) {
			this.province = cursor.getString(cursor.getColumnIndex(PubDBM.CCC_PROVINCE));
			this.city = cursor.getString(cursor.getColumnIndex(PubDBM.CCC_CITY));
			cursor.close();
		}
		setTextSummary(province, city);
	}

	private void setTextSummary(String province, String city) {
		if (province.equals(city)) {
			setText(city);
		} else {
			setText(province + " " + city);
		}
	}

	@Override
	public void onClick(View v) {
		dialog.show();
	}

	// 内部类
	class CitySelectDialog extends AlertDialog implements OnItemClickListener,OnClickListener {

		private Context context;
		private ListView lvProvince, lvCity;
		private Cursor pCursor, cCursor;
		private Button btClear;
		private SimpleCursorAdapter padapter, cadapter;

		public CitySelectDialog(Context context) {
			super(context);
			this.context = context;
		}

		public CitySelectDialog(Context context, int theme) {
			super(context, theme);
			this.context = context;
		}

		@Override
		protected void onCreate(Bundle savedInstanceState) {
			super.onCreate(savedInstanceState);
			// this.setView(R.layout.dialog_city_select);// 可能会有边框
			this.setContentView(R.layout.dialog_city_select);

			lvProvince = (ListView) findViewById(R.id.lvProvince);
			lvCity = (ListView) findViewById(R.id.lvCity);
			btClear = (Button) findViewById(R.id.btClear);
			btClear.setOnClickListener(this);
			
			if (dbm == null) {
				return;
			}

			pCursor = dbm.queryProvinceList();
			pCursor.moveToFirst();
			if (province == null) {
				province = pCursor.getString(pCursor.getColumnIndex(PubDBM.CCC_PROVINCE));
			}
			padapter = new SimpleCursorAdapter(context, R.layout.listview_item_city, pCursor, new String[] { PubDBM.CCC_PROVINCE },
					new int[] { R.id.tvSummary }, SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
			lvProvince.setAdapter(padapter);
			lvProvince.setOnItemClickListener(new OnItemClickListener() {
				@Override
				public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
					TextView tvSummary = (TextView) view.findViewById(R.id.tvSummary);
					tvSummary.setBackgroundResource(android.R.color.holo_blue_light);

					province = tvSummary.getText().toString();
					cadapter.changeCursor(dbm.queryCityList(province));
				}
			});
			lvProvince.setOnScrollListener(new OnScrollListener() {

				@Override
				public void onScrollStateChanged(AbsListView view, int scrollState) {
					// System.out.println("onScrollStateChanged"+scrollState);
				}

				@Override
				public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
					// System.out.println("onScroll:" + firstVisibleItem + "|" + visibleItemCount + "|" + totalItemCount);

					for (int i = 0; i < visibleItemCount; i++) {
						TextView tvSummary = (TextView) view.getChildAt(i).findViewById(R.id.tvSummary);
						// System.out.println("summary:" + tvSummary.getText().toString() + "||" + province);
						if (province.equals(tvSummary.getText().toString())) {
							tvSummary.setBackgroundResource(android.R.color.holo_blue_light);
						} else {
							tvSummary.setBackgroundColor(Color.TRANSPARENT);
						}
					}
				}
			});

			cCursor = dbm.queryCityList(province);
			cCursor.moveToFirst();
			code = cCursor.getString(cCursor.getColumnIndex(PubDBM.CCC_CODE));
			cadapter = new SimpleCursorAdapter(context, R.layout.listview_item_city, cCursor, new String[] { PubDBM.CCC_CITY,
					PubDBM.CCC_CODE }, new int[] { R.id.tvSummary, R.id.tvCode },
					SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
			lvCity.setAdapter(cadapter);
			lvCity.setOnItemClickListener(this);
		}

		@Override
		public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
			TextView tvSummary = (TextView) view.findViewById(R.id.tvSummary);
			TextView tvCode = (TextView) view.findViewById(R.id.tvCode);

			city = tvSummary.getText().toString();
			code = tvCode.getText().toString();

			setTextSummary(province, city);

			cancel();
		}

		@Override
		public void onClick(View v) {
			province = ""; 
			city = "";
			code = "";
			
			setText("");
			
			cancel();
		}
	}
}


代码还是比较简单的,由于时间仓促,二级目录内容的改变直接使用了

cadapter.changeCursor(dbm.queryCityList(province));
这个地方可能有更好的方法,不过城市的选择不会是经常选择的东西,应该问题不大。另外还有几个布局文件,都是一个界面上的东西了:

dialog_dity_select.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="360dp"
    android:layout_marginLeft="8dp"
    android:layout_marginRight="8dp"
    android:background="@android:color/holo_blue_dark"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="6dp"
        android:layout_marginBottom="8dp"
        android:gravity="center"
        android:text="城市选择"
        android:textColor="@android:color/holo_blue_bright"
        android:textSize="24sp" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/btClear"
        android:layout_below="@+id/tvTitle"
        android:padding="10dp" >

        <ListView
            android:id="@+id/lvProvince"
            android:layout_width="121dp"
            android:layout_height="246dp"
            android:layout_marginLeft="8dp"
            android:background="@android:color/holo_blue_bright"
            android:layout_alignParentLeft="true"
            android:divider="@null"/>

        <ListView
            android:id="@+id/lvCity"
            android:layout_width="121dp"
            android:layout_height="246dp"
            android:layout_marginRight="8dp"
            android:background="@android:color/holo_blue_bright"
            android:layout_alignParentRight="true"
            android:divider="@null"/>
    </RelativeLayout>

    <Button
        android:id="@+id/btClear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="8dp"
        android:text="清除选择"
        android:textColor="@android:color/holo_blue_bright" />

</RelativeLayout>
4、使用

这个就比较简单了,直接在xml中写上这个自定义的EditText,如下:

    <com.ttdevs.cityselect.util.CitySelect
        android:id="@+id/etCitySelect"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请选择城市" />

Activity中:

public class MainActivity extends Activity implements OnClickListener {
	private CitySelect csCity;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		csCity = (CitySelect) findViewById(R.id.etCitySelect);
		csCity.setCityCode("101060406"); // 吉林 四平
	}

	@Override
	public void onClick(View v) {
		Toast.makeText(getApplicationContext(), "你选择了:"+csCity.getCityCode(), Toast.LENGTH_LONG).show();
	}
}

5、总结

上图中效果是项目中的,里面图片资源不好拿出来共享,最终的效果如下图。


源码:下载






    
[3] asmack实现端到端的讯息回执(XEP-0184: Message Delivery Receipts)
    来源: 互联网  发布时间: 2014-02-18
asmack实现端到端的消息回执(XEP-0184: Message Delivery Receipts)

要想保证信息的传输,目前在smack/asmack + openfire架构上,我个人想到有两种实现方式:


1.端到端确保发送(类似短信)。

其实这个就是xmpp协议的XEP-0184: Message Delivery Receipts.

里边为了确保消息的到达,需要接收方返回回执,这样发送方就知道对方是否确切收到消息。

当然咯,接收回执过程中也可能出现断线,导致发送方收不到回执,而认为接收方没收到,再重新发的问题,这个需要接收方过滤掉重复的信息来解决。

其实这个协议,asmack在0.8.3版本就已经支持,具体什么版本开始,就懒得去研究了。

需要注意的是:这个是两个客户端之间的事情,即openfire什么也不用干,只要客户端都支持xep-0184就可以了。

 

下面简单说下xep-0184协议的交互内容:

发送方发送一个需要回执的消息:

<message from='northumberland@shakespeare.lit/westminster'
 					id='richard2-4.1.247'
					to='kingrichard@royalty.england.lit/throne'>
	<body>My lord, dispatch; read o'er these articles.</body>
	<request xmlns='urn:xmpp:receipts'/>
</message>

 

接收方收到消息后,返回的消息:

<message from='kingrichard@royalty.england.lit/throne'
 				id='bi29sg183b4v'
  			to='northumberland@shakespeare.lit/westminster'>
    <received xmlns='urn:xmpp:receipts' id='richard2-4.1.247'/>
</message>


注意回执的id要与接收的packetID对应。

 

好了,了解原理了。大家来看看asmack是怎么实现的。

http://bamboo.igniterealtime.org/browse/SMACK-TRUNK-59/commit 上说的好简单,实际上你不会成功的,因为asmack有bug,目前最新的0.8.5上也没解决。

下面跟大家介绍怎么使用消息回执及解决这个bug。

 

发送需要回执的消息前,调用

DeliveryReceiptManager.addDeliveryReceiptRequest(packet);
myConnection.sendPacket(packet);

来为你的packet添加<request xmlns='urn:xmpp:receipts'/>节点。

 

在初始化xmppconnection后,调用

DeliveryReceiptManager.getInstanceFor(myConnection)
             .enableAutoReceipts();

来设置自动进行回执,设置后,回执的事情就不用我们自己操心啦。

好了,要做的事情就这么点。额,,,本来应该就这么点。但是。。asmack有bug啊,他把request跟received都用同一个ExtensionProvider啦!!

证据在org.jivesoftware.smackx.ConfigureProviderManager类里边这两句:

	// XEP-184 Message Delivery Receipts
	pm.addExtensionProvider("received", "urn:xmpp:receipts", new DeliveryReceipt.Provider());
	pm.addExtensionProvider("request", "urn:xmpp:receipts", new DeliveryReceipt.Provider());

都用了同一个Provider,当然出问题了。就是接受者无法找到<request xmlns='urn:xmpp:receipts'/>节点,因为DeliveryReceipt.Provider()生成的是received节点。

于是乎,自动回执没有效果。

“改吧!。。”

“不改jar包行不行啊?”

“行啊!”

如下:

            sAndroid = SmackAndroid.init(cxtContext);
            ProviderManager pm = ProviderManager.getInstance();
            // add delivery receipts
            pm.addExtensionProvider(DeliveryReceipt.ELEMENT, DeliveryReceipt.NAMESPACE,
                    new DeliveryReceipt.Provider());
            pm.addExtensionProvider(DeliveryReceiptRequest.ELEMENT, DeliveryReceipt.NAMESPACE,
                    new DeliveryReceiptRequest.Provider());


将jar包中错误的设置,重新设置一下。这样,消息回执功能就大功告成啦。。

xep-0184里边还讲了如何判断客户端是否支持消息回执的问题,如果你有这样的需求,就自己去了解吧。


2.openfire服务端确保消息发送到达(待续)

我不知是否是由于MINA太旧的原因,导致openfire在nio发送的过程中,无法捕获发送异常,导致无法识别异常断线的客户端,也就不能准确的保存离线消息。
openfire目前用的MINA是1.1.7版本,可能是这个版本有问题。apache在这版本上进行了重大的调整和改造,版本号直接改成2.0.0。因此直接升级openfire的MINA包难度很大,连openfire开发者们都拖了这么些年不更新MINA版本。。

这个原因纯属个人猜测,于是这个方法走不通。有兴趣的同学可以试试,熟悉MINA的人应该能看出来。

 

既然底层机制不可变,那就只能通过自己的手法来处理了。


这个我目前在实践中,或许下一篇就会讲到。

1楼t8500071昨天 17:46csdn是越来越烂了。编辑节目难看不说。发个代码都被分割成这么多块

    
最新技术文章:
▪Android Touch事件分发过程详解 iis7站长之家
▪Android开发之注册登录方法示例
▪Android获取手机SIM卡运营商信息的方法
▪Android实现将已发送的短信写入短信数据库的...
▪Android发送短信功能代码
▪Android根据电话号码获得联系人头像实例代码
▪Android中GPS定位的用法实例
▪Android实现退出时关闭所有Activity的方法
▪Android实现文件的分割和组装
▪Android录音应用实例教程
▪Android双击返回键退出程序的实现方法
▪Android实现侦听电池状态显示、电量及充电动...
▪Android获取当前已连接的wifi信号强度的方法
▪Android实现动态显示或隐藏密码输入框的内容
▪根据USER-AGENT判断手机类型并跳转到相应的app...
▪Android Touch事件分发过程详解
▪Android中实现为TextView添加多个可点击的文本
▪Android程序设计之AIDL实例详解
▪Android显式启动与隐式启动Activity的区别介绍
▪Android按钮单击事件的四种常用写法总结
▪Android消息处理机制Looper和Handler详解
▪Android实现Back功能代码片段总结
▪Android实用的代码片段 常用代码总结
▪Android实现弹出键盘的方法
▪Android中通过view方式获取当前Activity的屏幕截...
▪Android提高之自定义Menu(TabMenu)实现方法
▪Android提高之多方向抽屉实现方法
▪Android提高之MediaPlayer播放网络音频的实现方法...
▪Android提高之MediaPlayer播放网络视频的实现方法...
▪Android提高之手游转电视游戏的模拟操控
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3