在網上找了這麼久也沒找到原文的主人,在此特别鳴謝本文作者:為android初學者和新手帶來如此詳細和精彩的文章
另:不要看本文長,耐心看下來會有很大收獲。
開始接觸學習android已經有3個禮拜了,一直都是對着android的sdk文檔寫Tutorials從Hello World到Notepad Tutorial算是初步入門了吧,剛好最近對微網誌感興趣就打算開發個android版本的新浪微部落格戶端作為練手項目,并且以随筆的方式詳細的記錄開發的全過程。本人對java語言以及eclipse Ide都是初次應用基本上屬于邊學邊用,做移動裝置上的東西也是第一次,總的來說屬于無基礎、無經驗、無天賦的純三無人員,還請廣大同學們多多給予指點。
開發第一件事情,那就是開發工具以及環境,我的配置是Eclipse Helios (3.6.1) + Adroid2.2,具體的環境搭建我就不羅嗦了,google一下一大堆,光部落格園裡都能搜到很多篇了。
開發第二件事情,既然是開發新浪的微部落格戶端,那就先去新浪申請微網誌賬号然後登陸後到新浪的開放平台,新浪的開放平台提供的新浪微網誌對外的api接口,在我的應用中建立一個新的應用擷取App Key和App Secret,這2個值後面會有用到先記錄下來。在新浪的開放平台中提供了開發文檔、SDK、接口測試工具等,本人決定直接通過新浪的Rest Api進行開發并不打算使用新浪提供的SDK,據說新浪提供的java版的SDK并不能直接用來進行android的開發需要進行一定的修改才能使用,隻是聽說我沒有試過不一定準确。
最後在說一下,我準備分為UI和功能兩部分分别進行說明講解,據我自己的情況大部分的時間都花在的UI的設計和實作上了,編碼倒反而工作量小多了,是以特别把UI部分分出來講。
最後還要在說一下,很抱歉上面内容基本上屬于廢話沒有什麼實質内容了但是既然是第一篇還是得象征性的交代一下,從下篇開始講具體的内容。
本軟體設定使用者第一個接觸到的功能就是頁面載入等待功能,這個功能對使用者來說就是一個持續1、2秒鐘的等待頁面,在使用者等待的同時程式做一些必要的檢查以及資料準備工作,載入頁面分為UI篇和功能篇,從表及裡首先是UI的實作,一個軟體除功能之外還得有一個光鮮的外表也是非常重要的,盡管本人設計水準一般但是還是親自操刀用ps先做了一下設計效果圖如下:
6 天前 上傳
下載下傳附件 (131.9 KB)
一、接下來的任務就是在android中實作這樣的效果顯示,從這個效果的設計分别把圖檔分成背景、版本号部分、軟體名稱和圖示、作者名稱和blog四個部分,按照這樣的思路把分别生成4張png的圖檔,背景部分考慮實作橫屏和豎屏切換額外添加一張橫屏背景圖,然後建立android工程,我這裡的名稱為MySinaWeibo,android版本勾選2.2,并且建立名為MainActivity的Activity作為整個軟體的起始頁面,然後把上面的這些圖檔儲存到項目的res/drawable-mdpi檔案夾下,關于res目錄下的drawable-mdpi、drawable-ldpi,、drawable-hdpi三個檔案夾的差別,mdpi 裡面主要放中等分辨率的圖檔,如HVGA (320x480)。ldpi裡面主要放低分辨率的圖檔,如QVGA (240x320)。hdpi裡面主要放高分辨率的圖檔,如WVGA (480x800),FWVGA (480x854)。android系統會根據機器的分辨率來分别到這幾個檔案夾裡面去找對應的圖檔,在開發程式時為了相容不同平台不同螢幕,建議各自檔案夾根據需求均存放不同版本圖檔,我這裡就不進行這麼多的考慮了。
二、完成圖檔資源的準備後接下就是layout檔案的編寫, 在res/layout檔案夾下建立main.xml檔案,這個layout采用LinearLayout控件作為頂層控件,然後用ImageView控件分别實作版本号圖檔頂部靠左對齊顯示、軟體名稱和圖示圖檔居中對齊、作者名稱和blog圖檔底部靠右對齊。注意在版本号圖檔顯示ImageView控件下面添加一個RelativeLayout控件作為軟體名稱和圖示圖檔ImageVIew和作者名稱和blog圖檔ImageView的父控件用來控制居中對齊已經底部對齊的實作,具體代碼如下:代碼
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ver"
android:layout_marginTop="15dip"
android:layout_marginLeft="15dip">
</ImageView>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/logo"
android:layout_centerInParent="true">
</ImageView>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/dev"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="5dip"
android:layout_marginBottom="35dip">
</ImageView>
</RelativeLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ver"
android:layout_marginTop="15dip"
android:layout_marginLeft="15dip">
</ImageView>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/logo"
android:layout_centerInParent="true">
</ImageView>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/dev"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="5dip"
android:layout_marginBottom="35dip">
</ImageView>
</RelativeLayout>
</LinearLayout>
三、在ec打開名為MainActivity的Activity源代碼檔案進行編輯,onCreate部分代碼如下:
view plaincopy to clipboardprint?
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
然後運作項目可以在模拟器中顯示,上面的幾個圖檔都按照設計的位置和效果進行顯示隻是整個頁面的背景還是黑色的,接下來就是背景部分的顯示實作,由于為了實作橫豎屏切換顯示,背景圖的顯示采用代碼進行控制顯示,首先用如下方法擷取目前手機是橫屏還是豎屏:
view plaincopy to clipboardprint?
//擷取螢幕方向
public static int ScreenOrient(Activity activity)
{
int orient = activity.getRequestedOrientation();
if(orient != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE && orient != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
//寬>高為橫屏,反正為豎屏
WindowManager windowManager = activity.getWindowManager();
Display display = windowManager.getDefaultDisplay();
int screenWidth = display.getWidth();
int screenHeight = display.getHeight();
orient = screenWidth < screenHeight ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
}
return orient;
}
//擷取螢幕方向
public static int ScreenOrient(Activity activity)
{
int orient = activity.getRequestedOrientation();
if(orient != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE && orient != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
//寬>高為橫屏,反正為豎屏
WindowManager windowManager = activity.getWindowManager();
Display display = windowManager.getDefaultDisplay();
int screenWidth = display.getWidth();
int screenHeight = display.getHeight();
orient = screenWidth < screenHeight ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
}
return orient;
}
然後編寫一個名為AutoBackground的公共方法用來實作螢幕背景的自動切換,後面的幾乎每一個功能頁面都需要用到這個方法
view plaincopy to clipboardprint?
public static void AutoBackground(Activity activity,View view,int Background_v, int Background_h)
{
int orient=ScreenOrient(activity);
if (orient == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { //縱向
view.setBackgroundResource(Background_v);
}else{ //橫向
view.setBackgroundResource(Background_h);
}
}
public static void AutoBackground(Activity activity,View view,int Background_v, int Background_h)
{
int orient=ScreenOrient(activity);
if (orient == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { //縱向
view.setBackgroundResource(Background_v);
}else{ //橫向
view.setBackgroundResource(Background_h);
}
}
完成上述兩方法後在 MainActivity的onCreate方法中調用AutoBackground方法進行螢幕自動切換:
view plaincopy to clipboardprint?
LinearLayout layout=(LinearLayout)findViewById(R.id.layout);
//背景自動适應
AndroidHelper.AutoBackground(this, layout, R.drawable.bg_v, R.drawable.bg_h);
LinearLayout layout=(LinearLayout)findViewById(R.id.layout);
//背景自動适應
AndroidHelper.AutoBackground(this, layout, R.drawable.bg_v, R.drawable.bg_h);
到此完成了載入頁面的UI部分的實作,測試運作模拟器中檢視效果,基本上跟最上面的設計效果圖相符,測試效果圖如下:
6 天前 上傳
下載下傳附件 (94.6 KB)
通過上一篇文章(android開發我的新浪微部落格戶端-載入頁面UI篇(1.1))已經完成了載入頁面的UI部分的實作,效果如上圖,接下來在上面的基礎上完成載入頁面的功能代碼。
6 天前 上傳
下載下傳附件 (94.6 KB) 首先說明一下新浪微網誌提供了OAuth和Base OAuth兩種認證方式(如果不知道什麼是OAuth和Base OAuth請自己google一下惡補,同時接下來的2篇随筆也會對這方面進行詳細的說明以及具體實作),本項目是采用OAuth認證方式,采用這種方式就需要有使用者的新浪UserID、Access Token、Access Secret這3樣東西才能自由便利的調用新浪的開放接口,本項目是這樣做的當使用者第一次使用軟體時進行授權認證擷取這3樣東西的時候存儲到sqlite庫中以便使用者下次使用時不需要重新進行繁瑣的授權認證操作直接從sqlite庫中讀取出來即可,由于這樣的需求載入頁面的功能設定是這樣:當使用者打開軟體顯示載入頁面時開始檢查sqlite庫中是否已經儲存有使用者的新浪微網誌的UserID号、Access Token、Access Secret的記錄,如果一條記錄都沒有那就說明使用者是第一次使用本軟體那麼跳到認證授權頁面進行授權認證操作(認證授權功能在接下來的兩篇中進行實作講解)擷取這3個值儲存到sqlite庫中,如果已經包括了記錄,那麼讀取這些記錄的UserID号、Access Token、Access Secret值然後根據這3個值調用新浪的api接口擷取這些記錄對應的使用者昵稱和使用者頭像圖示等資訊。
上面功能設定中涉及到sqlite資料庫的建立、資料表的建立、資料記錄的添加、資料記錄的讀取等操作,這裡建立名為SqliteHelper.java類檔案提供sqlite資料表的建立、更新等,代碼如下:
view plaincopy to clipboardprint?
public class SqliteHelper extends SQLiteOpenHelper{
//用來儲存
UserID、Access Token、Access Secret
的表名
public static final String TB_NAME="users";
public SqliteHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
}
//建立表
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS "+
TB_NAME+"("+
UserInfo.ID+" integer primary key,"+
UserInfo.USERID+" varchar,"+
UserInfo.TOKEN+" varchar,"+
UserInfo.TOKENSECRET+" varchar,"+
UserInfo.USERNAME+" varchar,"+
UserInfo.USERICON+" blob"+
")"
);
Log.e("Database","onCreate");
}
//更新表
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TB_NAME);
onCreate(db);
Log.e("Database","onUpgrade");
}
//更新列
public void updateColumn(SQLiteDatabase db, String oldColumn, String newColumn, String typeColumn){
try{
db.execSQL("ALTER TABLE " +
TB_NAME + " CHANGE " +
oldColumn + " "+ newColumn +
" " + typeColumn
);
}catch(Exception e){
e.printStackTrace();
}
}
}
public class SqliteHelper extends SQLiteOpenHelper{
//用來儲存
UserID、Access Token、Access Secret
的表名
public static final String TB_NAME="users";
public SqliteHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
}
//建立表
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS "+
TB_NAME+"("+
UserInfo.ID+" integer primary key,"+
UserInfo.USERID+" varchar,"+
UserInfo.TOKEN+" varchar,"+
UserInfo.TOKENSECRET+" varchar,"+
UserInfo.USERNAME+" varchar,"+
UserInfo.USERICON+" blob"+
")"
);
Log.e("Database","onCreate");
}
//更新表
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TB_NAME);
onCreate(db);
Log.e("Database","onUpgrade");
}
//更新列
public void updateColumn(SQLiteDatabase db, String oldColumn, String newColumn, String typeColumn){
try{
db.execSQL("ALTER TABLE " +
TB_NAME + " CHANGE " +
oldColumn + " "+ newColumn +
" " + typeColumn
);
}catch(Exception e){
e.printStackTrace();
}
}
}
接下來建立名為DataHelper.java類檔案實作使用者記錄的建立、更新、删除等,代碼如下
view plaincopy to clipboardprint?
public class DataHelper {
//資料庫名稱
private static String DB_NAME = "mysinaweibo.db";
//資料庫版本
private static int DB_VERSION = 2;
private SQLiteDatabase db;
private SqliteHelper dbHelper;
public DataHelper(Context context){
dbHelper=new SqliteHelper(context,DB_NAME, null, DB_VERSION);
db= dbHelper.getWritableDatabase();
}
public void Close()
{
db.close();
dbHelper.close();
}
//擷取users表中的UserID、Access Token、Access Secret的記錄
public List<UserInfo> GetUserList(Boolean isSimple)
{
List<UserInfo> userList = new ArrayList<UserInfo>();
Cursor cursor=db.query(SqliteHelper.TB_NAME, null, null, null, null, null, UserInfo.ID+" DESC");
cursor.moveToFirst();
while(!cursor.isAfterLast()&& (cursor.getString(1)!=null)){
UserInfo user=new UserInfo();
user.setId(cursor.getString(0));
user.setUserId(cursor.getString(1));
user.setToken(cursor.getString(2));
user.setTokenSecret(cursor.getString(3));
if(!isSimple){
user.setUserName(cursor.getString(4));
ByteArrayInputStream stream = new ByteArrayInputStream(cursor.getBlob(5));
Drawable icon= Drawable.createFromStream(stream, "image");
user.setUserIcon(icon);
}
userList.add(user);
cursor.moveToNext();
}
cursor.close();
return userList;
}
//判斷users表中的是否包含某個UserID的記錄
public Boolean HaveUserInfo(String UserId)
{
Boolean b=false;
Cursor cursor=db.query(SqliteHelper.TB_NAME, null, UserInfo.USERID + "=" + UserId, null, null, null,null);
b=cursor.moveToFirst();
Log.e("HaveUserInfo",b.toString());
cursor.close();
return b;
}
//更新users表的記錄,根據UserId更新使用者昵稱和使用者圖示
public int UpdateUserInfo(String userName,Bitmap userIcon,String UserId)
{
ContentValues values = new ContentValues();
values.put(UserInfo.USERNAME, userName);
// BLOB類型
final ByteArrayOutputStream os = new ByteArrayOutputStream();
// 将Bitmap壓縮成PNG編碼,品質為100%存儲
userIcon.compress(Bitmap.CompressFormat.PNG, 100, os);
// 構造SQLite的Content對象,這裡也可以使用raw
values.put(UserInfo.USERICON, os.toByteArray());
int id= db.update(SqliteHelper.TB_NAME, values, UserInfo.USERID + "=" + UserId, null);
Log.e("UpdateUserInfo2",id+"");
return id;
}
//更新users表的記錄
public int UpdateUserInfo(UserInfo user)
{
ContentValues values = new ContentValues();
values.put(UserInfo.USERID, user.getUserId());
values.put(UserInfo.TOKEN, user.getToken());
values.put(UserInfo.TOKENSECRET, user.getTokenSecret());
int id= db.update(SqliteHelper.TB_NAME, values, UserInfo.USERID + "=" + user.getUserId(), null);
Log.e("UpdateUserInfo",id+"");
return id;
}
//添加users表的記錄
public Long SaveUserInfo(UserInfo user)
{
ContentValues values = new ContentValues();
values.put(UserInfo.USERID, user.getUserId());
values.put(UserInfo.TOKEN, user.getToken());
values.put(UserInfo.TOKENSECRET, user.getTokenSecret());
Long uid = db.insert(SqliteHelper.TB_NAME, UserInfo.ID, values);
Log.e("SaveUserInfo",uid+"");
return uid;
}
//删除users表的記錄
public int DelUserInfo(String UserId){
int id= db.delete(SqliteHelper.TB_NAME, UserInfo.USERID +"="+UserId, null);
Log.e("DelUserInfo",id+"");
return id;
}
}
public class DataHelper {
//資料庫名稱
private static String DB_NAME = "mysinaweibo.db";
//資料庫版本
private static int DB_VERSION = 2;
private SQLiteDatabase db;
private SqliteHelper dbHelper;
public DataHelper(Context context){
dbHelper=new SqliteHelper(context,DB_NAME, null, DB_VERSION);
db= dbHelper.getWritableDatabase();
}
public void Close()
{
db.close();
dbHelper.close();
}
//擷取users表中的UserID、Access Token、Access Secret的記錄
public List<UserInfo> GetUserList(Boolean isSimple)
{
List<UserInfo> userList = new ArrayList<UserInfo>();
Cursor cursor=db.query(SqliteHelper.TB_NAME, null, null, null, null, null, UserInfo.ID+" DESC");
cursor.moveToFirst();
while(!cursor.isAfterLast()&& (cursor.getString(1)!=null)){
UserInfo user=new UserInfo();
user.setId(cursor.getString(0));
user.setUserId(cursor.getString(1));
user.setToken(cursor.getString(2));
user.setTokenSecret(cursor.getString(3));
if(!isSimple){
user.setUserName(cursor.getString(4));
ByteArrayInputStream stream = new ByteArrayInputStream(cursor.getBlob(5));
Drawable icon= Drawable.createFromStream(stream, "image");
user.setUserIcon(icon);
}
userList.add(user);
cursor.moveToNext();
}
cursor.close();
return userList;
}
//判斷users表中的是否包含某個UserID的記錄
public Boolean HaveUserInfo(String UserId)
{
Boolean b=false;
Cursor cursor=db.query(SqliteHelper.TB_NAME, null, UserInfo.USERID + "=" + UserId, null, null, null,null);
b=cursor.moveToFirst();
Log.e("HaveUserInfo",b.toString());
cursor.close();
return b;
}
//更新users表的記錄,根據UserId更新使用者昵稱和使用者圖示
public int UpdateUserInfo(String userName,Bitmap userIcon,String UserId)
{
ContentValues values = new ContentValues();
values.put(UserInfo.USERNAME, userName);
// BLOB類型
final ByteArrayOutputStream os = new ByteArrayOutputStream();
// 将Bitmap壓縮成PNG編碼,品質為100%存儲
userIcon.compress(Bitmap.CompressFormat.PNG, 100, os);
// 構造SQLite的Content對象,這裡也可以使用raw
values.put(UserInfo.USERICON, os.toByteArray());
int id= db.update(SqliteHelper.TB_NAME, values, UserInfo.USERID + "=" + UserId, null);
Log.e("UpdateUserInfo2",id+"");
return id;
}
//更新users表的記錄
public int UpdateUserInfo(UserInfo user)
{
ContentValues values = new ContentValues();
values.put(UserInfo.USERID, user.getUserId());
values.put(UserInfo.TOKEN, user.getToken());
values.put(UserInfo.TOKENSECRET, user.getTokenSecret());
int id= db.update(SqliteHelper.TB_NAME, values, UserInfo.USERID + "=" + user.getUserId(), null);
Log.e("UpdateUserInfo",id+"");
return id;
}
//添加users表的記錄
public Long SaveUserInfo(UserInfo user)
{
ContentValues values = new ContentValues();
values.put(UserInfo.USERID, user.getUserId());
values.put(UserInfo.TOKEN, user.getToken());
values.put(UserInfo.TOKENSECRET, user.getTokenSecret());
Long uid = db.insert(SqliteHelper.TB_NAME, UserInfo.ID, values);
Log.e("SaveUserInfo",uid+"");
return uid;
}
//删除users表的記錄
public int DelUserInfo(String UserId){
int id= db.delete(SqliteHelper.TB_NAME, UserInfo.USERID +"="+UserId, null);
Log.e("DelUserInfo",id+"");
return id;
}
}
完成上面的代碼後,我們需要在載入頁面中調用上面的方法實作sqlite庫中是否已經儲存有使用者的新浪微網誌的UserID号、Access Token、Access Secret的記錄的功能在MainActivity的onCreate方法添加代碼:
view plaincopy to clipboardprint?
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
......
//擷取賬号清單
dbHelper=new DataHelper(this);
List<UserInfo> userList= dbHelper.GetUserList(true);
if(userList.isEmpty())//如果為空說明第一次使用跳到AuthorizeActivity頁面進行OAuth認證
{
Intent intent = new Intent();
intent.setClass(MainActivity.this, AuthorizeActivity.class);
startActivity(intent);
}
else//如果不為空讀取這些記錄的UserID号、Access Token、Access Secret值
//然後根據這3個值調用新浪的api接口擷取這些記錄對應的使用者昵稱和使用者頭像圖示等資訊。
{
for(UserInfo user:userList){
......
}
}
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
......
//擷取賬号清單
dbHelper=new DataHelper(this);
List<UserInfo> userList= dbHelper.GetUserList(true);
if(userList.isEmpty())//如果為空說明第一次使用跳到AuthorizeActivity頁面進行OAuth認證
{
Intent intent = new Intent();
intent.setClass(MainActivity.this, AuthorizeActivity.class);
startActivity(intent);
}
else//如果不為空讀取這些記錄的UserID号、Access Token、Access Secret值
//然後根據這3個值調用新浪的api接口擷取這些記錄對應的使用者昵稱和使用者頭像圖示等資訊。
{
for(UserInfo user:userList){
......
}
}
}
本篇說說關于OAuth授權認證的事情,新浪開放api都必須在這個基礎上才能調用,是以有必要專門來講講,前面的文章中已經提到過關于新浪微網誌提供了OAuth和Base OAuth兩種認證方式,并且本項目采用OAuth認證方式,至于為什麼采用這個OAuth認證而不采用Base OAuth認證原因很簡單,自從Twitter隻支援OAuth認證方式以來,各大應用都紛紛轉向OAuth認證方式,而新浪微網誌的開放平台也将在近日停止Base OAuth的認證方式。
6 天前 上傳
下載下傳附件 (30.41 KB) OAuth的基本概念,OAUTH協定為使用者資源的授權提供了一個安全的、開放而又簡易的标準。與以往的授權方式不同之處是OAUTH的授權不會使第三方觸及到使用者的帳号資訊(如使用者名與密碼),即第三方無需使用使用者的使用者名與密碼就可以申請獲得該使用者資源的授權,是以OAUTH是安全的。同樣新浪微網誌提供OAuth認證也是為了保證使用者賬号和密碼的安全,在這裡通過OAuth建立普通新浪微網誌使用者、用戶端程式(我們正在開發的這個android用戶端程式)、新浪微網誌三者之間的互相信任關系,讓用戶端程式(我們正在開發的這個android用戶端程式)不需要知道使用者的賬号和密碼也能浏覽、釋出微網誌,這樣有效的保護了使用者賬号的安全性不需要把賬号密碼透露給用戶端程式又達到了通過用戶端程式寫微網誌看微網誌目的。這個是OAuth的作用。
結合新浪微網誌的OAuth認證來說說具體的功能實作,首先羅列一下關鍵字組,下面四組關鍵字跟我們接下來OAuth認證有非常大的關系。
第一組:(App Key和App Secret),這組參數就是本系列文本第一篇提到的建一個新的應用擷取App Key和App Secret。
第二組:(Request Token和Request Secret)
第三組:(oauth_verifier)
第四組:(user_id、Access Token和Access Secret)
新浪微網誌的OAuth認證過程,當使用者第一次使用本用戶端軟體時,用戶端程式用第一組作為參數向新浪微網誌發起請求,然後新浪微網誌經過驗證後傳回第二組參數給用戶端軟體同時表示新浪微網誌信任本用戶端軟體,當用戶端軟體擷取第二組參數時作為參數引導使用者浏覽器跳至新浪微網誌的授權頁面,然後使用者在新浪的這個授權頁面裡輸入自己的微網誌賬号和密碼進行授權,完成授權後根據用戶端設定的回調位址把第三組參數傳回給用戶端軟體并表示使用者也信任本用戶端軟體,接下用戶端軟體把第二組參數和第三組參數作為參數再次向新浪微網誌發起請求,然後新浪微網誌傳回第四組參數給用戶端軟體,第四組參數需要好好的儲存起來這個就是用來代替使用者的新浪賬号和密碼用的,在後面調用api時都需要。從這個過程來看使用者隻是在新浪微網誌的認證網頁輸入過賬戶和密碼并沒有在用戶端軟體裡輸入過賬戶和密碼,用戶端軟體隻儲存了第四組資料并沒有儲存使用者的賬戶和密碼,這樣有效的避免了賬戶和密碼透露給新浪微網誌之外的第三方應用程式,保證 了安全性。
本項目用為了友善開發采用了oauth-signpost開源項目進行OAuth認證開發,建立OAuth.java類檔案對OA進行簡單的封裝,OAuth類主要有RequestAccessToken、GetAccessToken、SignRequest三個方法,第一個方法RequestAccessToken就是上面過程中用來擷取第三組參數用的,GetAccessToken方法是用來擷取第四組參數用,SignRequest方法是用來調用api用。由于采用了oauth-signpost開源項目簡單了很多。具體代碼如下:
view plaincopy to clipboardprint?
public class OAuth {
private CommonsHttpOAuthConsumer httpOauthConsumer;
private OAuthProvider httpOauthprovider;
public String consumerKey;
public String consumerSecret;
public OAuth()
{
// 第一組:(App Key和App Secret)
// 這組參數就是本系列文本第一篇提到的建一個新的應用擷取App Key和App Secret。
this("3315495489","e2731e7grf592c0fd7fea32406f86e1b");
}
public OAuth(String consumerKey,String consumerSecret)
{
this.consumerKey=consumerKey;
this.consumerSecret=consumerSecret;
}
public Boolean RequestAccessToken(Activity activity,String callBackUrl){
Boolean ret=false;
try{
httpOauthConsumer = new CommonsHttpOAuthConsumer(consumerKey,consumerSecret);
httpOauthprovider = new DefaultOAuthProvider("http://api.t.sina.com.cn/oauth/request_token","http://api.t.sina.com.cn/oauth/access_token","http://api.t.sina.com.cn/oauth/authorize");
String authUrl = httpOauthprovider.retrieveRequestToken(httpOauthConsumer, callBackUrl);
activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl)));
ret=true;
}catch(Exception e){
}
return ret;
}
public UserInfo GetAccessToken(Intent intent){
UserInfo user=null;
Uri uri = intent.getData();
String verifier = uri.getQueryParameter(oauth.signpost.OAuth.OAUTH_VERIFIER);
try {
httpOauthprovider.setOAuth10a(true);
httpOauthprovider.retrieveAccessToken(httpOauthConsumer,verifier);
} catch (OAuthMessageSignerException ex) {
ex.printStackTrace();
} catch (OAuthNotAuthorizedException ex) {
ex.printStackTrace();
} catch (OAuthExpectationFailedException ex) {
ex.printStackTrace();
} catch (OAuthCommunicationException ex) {
ex.printStackTrace();
}
SortedSet<String> user_id= httpOauthprovider.getResponseParameters().get("user_id");
String userId=user_id.first();
String userKey = httpOauthConsumer.getToken();
String userSecret = httpOauthConsumer.getTokenSecret();
user=new UserInfo();
user.setUserId(userId);
user.setToken(userKey);
user.setTokenSecret(userSecret);
return user;
}
public HttpResponse SignRequest(String token,String tokenSecret,String url,List params)
{
HttpPost post = new HttpPost(url);
//HttpClient httpClient = null;
try{
post.setEntity(new UrlEncodedFormEntity(params,HTTP.UTF_8));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//關閉Expect:100-Continue握手
//100-Continue握手需謹慎使用,因為遇到不支援HTTP/1.1協定的伺服器或者代理時會引起問題
post.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
return SignRequest(token,tokenSecret,post);
}
public HttpResponse SignRequest(String token,String tokenSecret,HttpPost post){
httpOauthConsumer = new CommonsHttpOAuthConsumer(consumerKey,consumerSecret);
httpOauthConsumer.setTokenWithSecret(token,tokenSecret);
HttpResponse response = null;
try {
httpOauthConsumer.sign(post);
} catch (OAuthMessageSignerException e) {
e.printStackTrace();
} catch (OAuthExpectationFailedException e) {
e.printStackTrace();
} catch (OAuthCommunicationException e) {
e.printStackTrace();
}
//取得HTTP response
try {
response = new DefaultHttpClient().execute(post);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
}
public class OAuth {
private CommonsHttpOAuthConsumer httpOauthConsumer;
private OAuthProvider httpOauthprovider;
public String consumerKey;
public String consumerSecret;
public OAuth()
{
// 第一組:(App Key和App Secret)
// 這組參數就是本系列文本第一篇提到的建一個新的應用擷取App Key和App Secret。
this("3315495489","e2731e7grf592c0fd7fea32406f86e1b");
}
public OAuth(String consumerKey,String consumerSecret)
{
this.consumerKey=consumerKey;
this.consumerSecret=consumerSecret;
}
public Boolean RequestAccessToken(Activity activity,String callBackUrl){
Boolean ret=false;
try{
httpOauthConsumer = new CommonsHttpOAuthConsumer(consumerKey,consumerSecret);
httpOauthprovider = new DefaultOAuthProvider("http://api.t.sina.com.cn/oauth/request_token","http://api.t.sina.com.cn/oauth/access_token","http://api.t.sina.com.cn/oauth/authorize");
String authUrl = httpOauthprovider.retrieveRequestToken(httpOauthConsumer, callBackUrl);
activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl)));
ret=true;
}catch(Exception e){
}
return ret;
}
public UserInfo GetAccessToken(Intent intent){
UserInfo user=null;
Uri uri = intent.getData();
String verifier = uri.getQueryParameter(oauth.signpost.OAuth.OAUTH_VERIFIER);
try {
httpOauthprovider.setOAuth10a(true);
httpOauthprovider.retrieveAccessToken(httpOauthConsumer,verifier);
} catch (OAuthMessageSignerException ex) {
ex.printStackTrace();
} catch (OAuthNotAuthorizedException ex) {
ex.printStackTrace();
} catch (OAuthExpectationFailedException ex) {
ex.printStackTrace();
} catch (OAuthCommunicationException ex) {
ex.printStackTrace();
}
SortedSet<String> user_id= httpOauthprovider.getResponseParameters().get("user_id");
String userId=user_id.first();
String userKey = httpOauthConsumer.getToken();
String userSecret = httpOauthConsumer.getTokenSecret();
user=new UserInfo();
user.setUserId(userId);
user.setToken(userKey);
user.setTokenSecret(userSecret);
return user;
}
public HttpResponse SignRequest(String token,String tokenSecret,String url,List params)
{
HttpPost post = new HttpPost(url);
//HttpClient httpClient = null;
try{
post.setEntity(new UrlEncodedFormEntity(params,HTTP.UTF_8));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//關閉Expect:100-Continue握手
//100-Continue握手需謹慎使用,因為遇到不支援HTTP/1.1協定的伺服器或者代理時會引起問題
post.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
return SignRequest(token,tokenSecret,post);
}
public HttpResponse SignRequest(String token,String tokenSecret,HttpPost post){
httpOauthConsumer = new CommonsHttpOAuthConsumer(consumerKey,consumerSecret);
httpOauthConsumer.setTokenWithSecret(token,tokenSecret);
HttpResponse response = null;
try {
httpOauthConsumer.sign(post);
} catch (OAuthMessageSignerException e) {
e.printStackTrace();
} catch (OAuthExpectationFailedException e) {
e.printStackTrace();
} catch (OAuthCommunicationException e) {
e.printStackTrace();
}
//取得HTTP response
try {
response = new DefaultHttpClient().execute(post);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
}
這樣就完成了OAuth功能類的開發,後面都會用到這個類相關的方法。本篇到這裡就算是完結請繼續關注後面的文章。
上一篇講了講OAuth授權認證的事情,大概的介紹了OAuth的原理,并且完成了一個OAuth.java的類庫,提供了幾個OAuth認證必要的方法,本篇開始具體講本項目的使用者授權功能,使用者授權頁面是當使用者第一次使用本軟體的時候自動從載入頁面跳轉過來的顯示的頁面,涉及OAuth認證相關都是在上一篇的OAuth.java的類基礎上開發。使用者授權頁面分為UI篇和功能篇兩篇,本篇先來講講UI的實作,這次就不貼PS的效果圖了直接貼實作後的功能截圖如下:
6 天前 上傳
下載下傳附件 (84.47 KB) 看上面的圖,其實這個頁面的UI實作不複雜,首先是背景部分的實作這個參考 android開發我的新浪微部落格戶端-載入頁面UI篇(1.1),重點來講講這個半透明的彈出對話框視窗是如何實作的,首先建立名為AuthorizeActivity.java的Activity,并且在AndroidManifest.xml檔案中添加這個Activity,這樣這個Activity才能被使用,接下來為這個Activity建立名為authorize.xml的Layout,這個Layout很簡單隻負責logo小圖示顯示,背景部分和透明視窗都是有代碼來實作,是以非常簡單參考 android開發我的新浪微部落格戶端-載入頁面UI篇(1.1)。
完成Layout建立後在AuthorizeActivity的onCreate方法添加如下代碼,設定authorize.xml為AuthorizeActivity的頁面Layout:
view plaincopy to clipboardprint?
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.authorize);
.......
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.authorize);
.......
}
接下來是本文的重點部分,半透明彈窗用Dialog控件進行實作,首先為這個半透明彈窗建立一個名為dialog.xml的Layout,這個Layout主要是對4個元素進行布局,如圖所示分别為i小圖示、資訊提示、中間文字、開始按鈕,首先用LinearLayout對i小圖示和資訊提示進行水準布局,中間文字以一個TextView跟在下面,對于開始按鈕是用RelativeLayout進行底部對齊顯示。具體代碼如下:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dip">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/info_icon">
</ImageView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="資訊提示"
android:textSize="13px"
android:textColor="#219ac6"
android:layout_marginLeft="5dip">
</TextView>
</LinearLayout>
<TextView
android:id="@+id/text_info"
android:layout_marginTop="6px"
android:layout_width="200px"
android:layout_height="wrap_content"
android:textColor="#686767"
android:textSize="14px"
android:text="第一次使用需要輸入您的新浪微網誌賬号和密碼進行登入授權">
</TextView>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="40px">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true">
<ImageButton
android:id="@+id/btn_start"
android:layout_width="80px"
android:layout_height="31px"
android:src="@drawable/btn_start_selector">
</ImageButton>
<ImageButton
android:id="@+id/btn_cancel"
android:layout_width="80px"
android:layout_height="31px"
android:layout_marginLeft="8px"
android:src="@drawable/btn_cancel_selector">
</ImageButton>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dip">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/info_icon">
</ImageView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="資訊提示"
android:textSize="13px"
android:textColor="#219ac6"
android:layout_marginLeft="5dip">
</TextView>
</LinearLayout>
<TextView
android:id="@+id/text_info"
android:layout_marginTop="6px"
android:layout_width="200px"
android:layout_height="wrap_content"
android:textColor="#686767"
android:textSize="14px"
android:text="第一次使用需要輸入您的新浪微網誌賬号和密碼進行登入授權">
</TextView>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="40px">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true">
<ImageButton
android:id="@+id/btn_start"
android:layout_width="80px"
android:layout_height="31px"
android:src="@drawable/btn_start_selector">
</ImageButton>
<ImageButton
android:id="@+id/btn_cancel"
android:layout_width="80px"
android:layout_height="31px"
android:layout_marginLeft="8px"
android:src="@drawable/btn_cancel_selector">
</ImageButton>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
完成了半透明彈窗的Layout定義接下來我們要做的就是為它寫一個自定義樣式來實作我們想要的顯示效果,首先我們需準備一個圓角的半透明png圖檔名為dia_bg.png并且添加到drawable中,接下來再res/values檔案夾建立名為 dialogStyle.xml的resources樣式檔案,具體代碼如下:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?>
<resources>
<mce:style name="dialog" parent="@android:style/Theme.Dialog"><!--
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@drawable/dia_bg</item>
<item name="android:backgroundDimEnabled">false</item>
--></mce:style><style name="dialog" parent="@android:style/Theme.Dialog" mce_bogus="1"><item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@drawable/dia_bg</item>
<item name="android:backgroundDimEnabled">false</item></style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<mce:style name="dialog" parent="@android:style/Theme.Dialog"><!--
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@drawable/dia_bg</item>
<item name="android:backgroundDimEnabled">false</item>
--></mce:style><style name="dialog" parent="@android:style/Theme.Dialog" mce_bogus="1"><item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@drawable/dia_bg</item>
<item name="android:backgroundDimEnabled">false</item></style>
</resources>
這個樣式檔案的說明如下
parent="@android:style/Theme.Dialog" :在系統Dialog樣式基礎上,相當于繼承系統樣式
<item name="android:windowFrame">@null</item> :Dialog的windowFrame框為無
<item name="android:windowIsFloating">true</item>:是否浮現在activity之上
<item name="android:windowIsTranslucent">false</item>:是否半透明
<item name="android:windowNoTitle">true</item>:是否顯示title
<item name="android:windowBackground">@drawable/dia_bg</item>:設定dialog的背景
<item name="android:backgroundDimEnabled">false</item>: 背景是否模糊顯示
接下來寫java代碼把這個半透明彈窗顯示出來,在AuthorizeActivity的onCreate方法添加如下代碼:
view plaincopy to clipboardprint?
......
View diaView=View.inflate(this, R.layout.dialog, null);
dialog=new Dialog(AuthorizeActivity.this,R.style.dialog);
dialog.setContentView(diaView);
dialog.show();
......
......
View diaView=View.inflate(this, R.layout.dialog, null);
dialog=new Dialog(AuthorizeActivity.this,R.style.dialog);
dialog.setContentView(diaView);
dialog.show();
......
最後運作檢視效果,到這裡我們的任務已經完成了。請關注下一篇功能篇。
android開發我的新浪微部落格戶端-使用者授權頁面功能篇(3.2)
6 天前 上傳
下載下傳附件 (30.41 KB)
6 天前 上傳
下載下傳附件 (84.47 KB)
在上一篇實作了使用者授權頁面的UI,如上圖,接下來要做的就是在這個基礎上完成功能部分真正實作使用者的授權認證,這一篇是android開發我的新浪微部落格戶端-OAuth篇(2.1)的具體應用篇原理就不多解釋了不懂的看OAuth篇即可。認證過程從點選開始按鈕然後跳轉到新浪的授權頁面,接着使用者在新浪的頁面裡輸入自己的賬戶和密碼确定後傳回使用者授權頁面。首先給開始按鈕添加點選事件代碼,代碼中主要是調用我們前面android開發我的新浪微部落格戶端-OAuth篇(2.1)完成的OAuth類的RequestAccessToken方法用來擷取oauth_verifier,具體代碼如下:
view plaincopy to clipboardprint?
ImageButton stratBtn=(ImageButton)diaView.findViewById(R.id.btn_start);
stratBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
auth=new OAuth();
auth.RequestAccessToken(AuthorizeActivity.this, CallBackUrl);
}
});
ImageButton stratBtn=(ImageButton)diaView.findViewById(R.id.btn_start);
stratBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
auth=new OAuth();
auth.RequestAccessToken(AuthorizeActivity.this, CallBackUrl);
}
});
上面的代碼中重點來說明一下 RequestAccessToken方法的第二參數CallBackUrl,這個參數是使用者在新浪的頁面中輸入賬戶密碼後完成認證後傳回的位址,我這裡是這樣設定的CallBackUrl = "myapp://AuthorizeActivity",在AndroidManifest.xml中配置給AuthorizeActivity添加如下配置把myapp://AuthorizeActivity指向到AuthorizeActivity,這樣當頁面傳回到AuthorizeActivity中就可以擷取到傳過來的oauth_verifier參數。
view plaincopy to clipboardprint?
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="AuthorizeActivity" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="AuthorizeActivity" />
</intent-filter>
再AuthorizeActivity如果來接收傳回的oauth_verifier參數呢?接下來在AuthorizeActivity添加如下方法:
view plaincopy to clipboardprint?
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//在這裡處理擷取傳回的oauth_verifier參數
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//在這裡處理擷取傳回的oauth_verifier參數
}
關于onNewIntent的說明是這樣的,onCreate是用來建立一個Activity也就是建立一個窗體,但一個Activty處于任務棧的頂端,若再次調用startActivity去建立它,則不會再次建立。若你想利用已有的Acivity去處理别的Intent時,你就可以利用onNewIntent來處理。在onNewIntent裡面就會獲得新的Intent,在這裡AuthorizeActivity是屬于已有的Acivity,是以需要onNewIntent來處理接收傳回的參數,擷取oauth_verifier參數後OAuth還沒有結束從android開發我的新浪微部落格戶端-OAuth篇(2.1)描述來看還需要進行根據這個參數繼續向新浪微網誌請求擷取User_id、Access Token和Access Secret,在這裡我把這些操作全部寫在了GetAccessToken方法中。在onNewIntent添加如下代碼:
view plaincopy to clipboardprint?
UserInfo user= auth.GetAccessToken(intent);
if(user!=null){
DataHelper helper=new DataHelper(this);
String uid=user.getUserId();
if(helper.HaveUserInfo(uid))
{
helper.UpdateUserInfo(user);
Log.e("UserInfo", "update");
}else
{
helper.SaveUserInfo(user);
Log.e("UserInfo", "add");
}
}
UserInfo user= auth.GetAccessToken(intent);
if(user!=null){
DataHelper helper=new DataHelper(this);
String uid=user.getUserId();
if(helper.HaveUserInfo(uid))
{
helper.UpdateUserInfo(user);
Log.e("UserInfo", "update");
}else
{
helper.SaveUserInfo(user);
Log.e("UserInfo", "add");
}
}
通過上面的代碼完成了User_id、Access Token和Access Secret 擷取并且儲存到了sqlite庫中,這樣就完成了使用者的OAuth認證,當需要調用新浪的api時隻需要去sqlite庫中找該使用者的User_id、Access Token和Access Secret 即可。到這裡本篇就結束了,請關注下一篇。
android開發我的新浪微部落格戶端-登入頁面UI篇(4.1)
6 天前 上傳
下載下傳附件 (83.17 KB)
6 天前 上傳
下載下傳附件 (85.15 KB)
首先回顧一下功能流程當使用者開啟軟體顯示載入頁面時程式首先去sqlite庫查詢是否已經儲存有使用者的新浪微網誌的UserID号、Access Token、Access Secret的記錄如果沒有一條記錄那麼跳轉到使用者授權功能頁面,這個已經由上面兩篇文章實作了,如果有記錄那麼頁面跳轉到使用者登入頁面,也就是本篇以及下篇要實作的功能,本篇講UI的實作,本項目支援多微網誌賬号了,也就是使用者可以設定多個微網誌賬号,登入的時候選擇其中的一個登入,具體效果如上圖,建立名LoginActivity.java的Activity并且在AndroidManifest.xml中進行相應配置,這個頁面就是我們要實作的使用者登入頁面。
看上面的效果,首先頁面分3部分實作,背景部分、底部菜單部分、使用者選擇以及頭像顯示部分,首先在res/layout的目錄下建立名為login.xml的layout,然後根據頁面顯示要求編寫如下的布局控制:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/logo_s"
android:layout_marginTop="5dip"
android:layout_marginLeft="5dip">
</ImageView>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:id="@+id/iconBtn"
android:layout_width="90px"
android:layout_height="80px"
android:background="@drawable/icon_selector"
android:layout_above="@+id/selectLayout"
android:layout_centerHorizontal="true"
android:layout_marginBottom="20dip">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
</ImageView>
</RelativeLayout>
<RelativeLayout
android:id="@+id/selectLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<EditText
android:id="@+id/iconSelect"
android:layout_width="200px"
android:layout_height="wrap_content"
android:maxLength="10"
android:paddingLeft="20px"
android:editable="false"
android:enabled="false"
android:textSize="13px"
android:background="@drawable/input_over" >
</EditText>
<ImageButton
android:id="@+id/iconSelectBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="1.0dip"
android:layout_alignTop="@+id/iconSelect"
android:layout_alignRight="@+id/iconSelect"
android:layout_alignBottom="@+id/iconSelect"
android:background="@drawable/more_selector" >
</ImageButton>
<ImageButton
android:id="@+id/login"
android:layout_width="40px"
android:layout_height="40px"
android:layout_marginLeft="5dip"
android:layout_alignTop="@+id/iconSelectBtn"
android:layout_toRightOf="@+id/iconSelectBtn"
android:layout_alignBottom="@+id/iconSelectBtn"
android:background="@drawable/btn_in_selector" >
</ImageButton>
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="44dip"
android:layout_alignParentBottom="true"
android:background="#BB768e95">
<LinearLayout
android:id="@+id/addLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentLeft="true"
android:gravity="center"
android:layout_marginTop="3px">
<ImageButton
android:id="@+id/addIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/add_selector">
</ImageButton>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
android:textSize="12px"
android:text="添加賬号">
</TextView>
</LinearLayout>
<LinearLayout
android:id="@+id/exitLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_centerInParent="true"
android:gravity="center"
android:layout_marginTop="3px">
<ImageButton
android:id="@+id/exitIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/exit_selector">
</ImageButton>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
android:textSize="12px"
android:text="退出軟體">
</TextView>
</LinearLayout>
<LinearLayout
android:id="@+id/delLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentRight="true"
android:gravity="center"
android:layout_marginTop="3px">
<ImageButton
android:id="@+id/delIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/del_selector">
</ImageButton>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
android:textSize="12px"
android:text="删除賬号">
</TextView>
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/logo_s"
android:layout_marginTop="5dip"
android:layout_marginLeft="5dip">
</ImageView>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:id="@+id/iconBtn"
android:layout_width="90px"
android:layout_height="80px"
android:background="@drawable/icon_selector"
android:layout_above="@+id/selectLayout"
android:layout_centerHorizontal="true"
android:layout_marginBottom="20dip">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
</ImageView>
</RelativeLayout>
<RelativeLayout
android:id="@+id/selectLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<EditText
android:id="@+id/iconSelect"
android:layout_width="200px"
android:layout_height="wrap_content"
android:maxLength="10"
android:paddingLeft="20px"
android:editable="false"
android:enabled="false"
android:textSize="13px"
android:background="@drawable/input_over" >
</EditText>
<ImageButton
android:id="@+id/iconSelectBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="1.0dip"
android:layout_alignTop="@+id/iconSelect"
android:layout_alignRight="@+id/iconSelect"
android:layout_alignBottom="@+id/iconSelect"
android:background="@drawable/more_selector" >
</ImageButton>
<ImageButton
android:id="@+id/login"
android:layout_width="40px"
android:layout_height="40px"
android:layout_marginLeft="5dip"
android:layout_alignTop="@+id/iconSelectBtn"
android:layout_toRightOf="@+id/iconSelectBtn"
android:layout_alignBottom="@+id/iconSelectBtn"
android:background="@drawable/btn_in_selector" >
</ImageButton>
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="44dip"
android:layout_alignParentBottom="true"
android:background="#BB768e95">
<LinearLayout
android:id="@+id/addLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentLeft="true"
android:gravity="center"
android:layout_marginTop="3px">
<ImageButton
android:id="@+id/addIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/add_selector">
</ImageButton>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
android:textSize="12px"
android:text="添加賬号">
</TextView>
</LinearLayout>
<LinearLayout
android:id="@+id/exitLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_centerInParent="true"
android:gravity="center"
android:layout_marginTop="3px">
<ImageButton
android:id="@+id/exitIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/exit_selector">
</ImageButton>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
android:textSize="12px"
android:text="退出軟體">
</TextView>
</LinearLayout>
<LinearLayout
android:id="@+id/delLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentRight="true"
android:gravity="center"
android:layout_marginTop="3px">
<ImageButton
android:id="@+id/delIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/del_selector">
</ImageButton>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"
android:textSize="12px"
android:text="删除賬号">
</TextView>
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
</LinearLayout>
正對上面的login.xml的layout進行一下說明,背景部分前面已經講過了這裡也就不重複。
底部菜單實作,原本我是采用GridView實作的非常的友善但是後來由于顯示位置不好控制改成了用RelativeLayout和LinearLayout嵌套的方式,實作的比較土但是達到了顯示需求,首先是一個最外面的RelativeLayout目的是用來實作底部對齊顯示,并且把這個RelativeLayout的背景設定為淺藍色半透明的效果,關鍵這2行:android:layout_alignParentBottom="true"和android:background="#BB768e95"。然後是在RelativeLayout内部添加3個LinearLayout分别是用來顯示添加賬号、退出軟體、删除賬号3個功能按鈕菜單,并且分别設定為左對齊、居中對齊、右對齊,3個LinearLayout都設定為垂直布局androidrientation="vertical",然後每LinearLayout添加相應的圖檔和文字。
使用者選擇以及頭像顯示部分,這塊分成3小塊,用來顯示使用者頭像的ImageView、用來顯示使用者名字并且點選可以出現選擇清單的EditText、用來點選進入目前選擇使用者首頁的功能按鈕ImageButton,這3小塊的布局實作也是采用elativeLayout和LinearLayout互相嵌套配合的方式實作的具體參考login.xml。這裡重點說說這個賬号選擇清單彈出視窗的實作,當點選下拉箭頭按鈕的時候彈出并顯示,這個是用Dialog控件實作,首先準備好圓角的半透明背景圖mask_bg.png然後添加到res/drawable-mdpi檔案夾下,接着自定義一個Dialog樣式檔案,在res/values目錄下建立名為dialogStyles2.xml的resources檔案,在使用者授權驗證頁面的時候我們也自定義過類似的Dialog的樣式,具體解釋可以參考前面的戶授權驗證頁面功能,内容如下:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?>
<resources>
<mce:style name="dialog2" parent="@android:style/Theme.Dialog"><!--
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@drawable/mask_bg</item>
<item name="android:backgroundDimEnabled">true</item>
--></mce:style><style name="dialog2" parent="@android:style/Theme.Dialog" mce_bogus="1"><item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@drawable/mask_bg</item>
<item name="android:backgroundDimEnabled">true</item></style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<mce:style name="dialog2" parent="@android:style/Theme.Dialog"><!--
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@drawable/mask_bg</item>
<item name="android:backgroundDimEnabled">true</item>
--></mce:style><style name="dialog2" parent="@android:style/Theme.Dialog" mce_bogus="1"><item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@drawable/mask_bg</item>
<item name="android:backgroundDimEnabled">true</item></style>
</resources>
接下來還需要定義選擇清單的layout,建立名為dialog2.xml的layout檔案,内容如下:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="4dip">
<ListView
android:id="@+id/list"
android:layout_width="240px"
android:layout_height="220px"
android:divider="#f1f2f2"
android:dividerHeight="1px"
android:layout_margin="5px"
android:background="#ffffff"
android:cacheColorHint="#00000000">
</ListView>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="4dip">
<ListView
android:id="@+id/list"
android:layout_width="240px"
android:layout_height="220px"
android:divider="#f1f2f2"
android:dividerHeight="1px"
android:layout_margin="5px"
android:background="#ffffff"
android:cacheColorHint="#00000000">
</ListView>
</LinearLayout>
完成了layout和樣式檔案的編寫,接下來就是把dialogStyles2.xml樣式檔案和dialog2.xml的清單layout用起來,當點選id為iconSelectBtn的ImageButton時顯示使用者選擇視窗,在LoginActivity的onCreate方法中添加如下代碼:
view plaincopy to clipboardprint?
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
LinearLayout layout=(LinearLayout)findViewById(R.id.layout);
//背景自動适應
AndroidHelper.AutoBackground(this, layout, R.drawable.bg_v, R.drawable.bg_h);
ImageButton iconSelectBtn=(ImageButton)findViewById(R.id.iconSelectBtn);
iconSelectBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
View diaView=View.inflate(LoginActivity.this, R.layout.dialog2, null);
dialog=new Dialog(LoginActivity.this,R.style.dialog2);
dialog.setContentView(diaView);
dialog.show();
......
}
});
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
LinearLayout layout=(LinearLayout)findViewById(R.id.layout);
//背景自動适應
AndroidHelper.AutoBackground(this, layout, R.drawable.bg_v, R.drawable.bg_h);
ImageButton iconSelectBtn=(ImageButton)findViewById(R.id.iconSelectBtn);
iconSelectBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
View diaView=View.inflate(LoginActivity.this, R.layout.dialog2, null);
dialog=new Dialog(LoginActivity.this,R.style.dialog2);
dialog.setContentView(diaView);
dialog.show();
......
}
});
到這裡登入的UI部分就實作的差不多了,剩下的都是一些功能部分代碼用來實作從sqlite中賬号清單的擷取,以及點選選擇等互動操作等,這些在下一篇中來繼續的講。
android開發我的新浪微部落格戶端-登入頁面功能篇(4.2)
上一篇中完成了如上圖的UI部分的實作,現在繼續來講功能的實作,使用者登入操作主要就是賬号清單顯示和選擇賬号登入兩個功能其他的都是些簡單的輔助功能,首先是點選id為iconSelectBtn的ImageButton時顯示使用者選擇視窗,這個時候去資料庫中擷取賬号記錄然後在選擇視窗中以清單方式顯示出來,通過上一篇已經知道Id為list的ListView控件來顯示賬号清單,首先是從資料庫中擷取所有的賬戶記錄然後設定預設選中的使用者賬号代碼如下:
view plaincopy to clipboardprint?
private void initUser(){
//擷取賬号清單
dbHelper=new DataHelper(this);
userList = dbHelper.GetUserList(false);
if(userList.isEmpty())
{
Intent intent = new Intent();
intent.setClass(LoginActivity.this, AuthorizeActivity.class);
startActivity(intent);
}
else
{
SharedPreferences preferences = getSharedPreferences(Select_Name, Activity.MODE_PRIVATE);
String str= preferences.getString("name", "");
UserInfo user=null;
if(str!="")
{
user=GetUserByName(str);
}
if(user==null)
{
user=userList.get(0);
}
icon.setImageDrawable(user.getUserIcon());
iconSelect.setText(user.getUserName());
}
}
private void initUser(){
//擷取賬号清單
dbHelper=new DataHelper(this);
userList = dbHelper.GetUserList(false);
if(userList.isEmpty())
{
Intent intent = new Intent();
intent.setClass(LoginActivity.this, AuthorizeActivity.class);
startActivity(intent);
}
else
{
SharedPreferences preferences = getSharedPreferences(Select_Name, Activity.MODE_PRIVATE);
String str= preferences.getString("name", "");
UserInfo user=null;
if(str!="")
{
user=GetUserByName(str);
}
if(user==null)
{
user=userList.get(0);
}
icon.setImageDrawable(user.getUserIcon());
iconSelect.setText(user.getUserName());
}
}
這個initUser() 初始賬号的方法在LoginActivity的onCreate中調用,主要完成兩件事情,第一件擷取通過userList = dbHelper.GetUserList(false);擷取所有的賬戶記錄,關于DataHelper前面已經有說過了,如果擷取的使用者記錄為空那麼就跳轉到使用者授權功能頁面讓使用者添加賬号,如果不為空那麼通過SharedPreferences去讀取使用者上一次選擇的賬号名稱,如果沒有或者資料庫裡賬号記錄不包括這個賬戶名稱那麼預設顯示記錄的第一個賬号和頭像,如果有那麼顯示這個賬戶的名稱和頭像。關于SharedPreferences,是android提供給開發者用來存儲一些簡單的資料用的,非常友善類似于網站的Cookie,在這裡我就是用這個來儲存上一次使用者選擇的是哪個賬号,非常實用。
接下類首先為Id為list的ListView控件準備資料Adapter,這個Adapter非常簡單就是普通的adapter繼承BaseAdapter即可,代碼如下:
view plaincopy to clipboardprint?
public class UserAdapater extends BaseAdapter{
@Override
public int getCount() {
return userList.size();
}
@Override
public Object getItem(int position) {
return userList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.item_user, null);
ImageView iv = (ImageView) convertView.findViewById(R.id.iconImg);
TextView tv = (TextView) convertView.findViewById(R.id.showName);
UserInfo user = userList.get(position);
try {
//設定圖檔顯示
iv.setImageDrawable(user.getUserIcon());
//設定資訊
tv.setText(user.getUserName());
} catch (Exception e) {
e.printStackTrace();
}
return convertView;
}
public class UserAdapater extends BaseAdapter{
@Override
public int getCount() {
return userList.size();
}
@Override
public Object getItem(int position) {
return userList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.item_user, null);
ImageView iv = (ImageView) convertView.findViewById(R.id.iconImg);
TextView tv = (TextView) convertView.findViewById(R.id.showName);
UserInfo user = userList.get(position);
try {
//設定圖檔顯示
iv.setImageDrawable(user.getUserIcon());
//設定資訊
tv.setText(user.getUserName());
} catch (Exception e) {
e.printStackTrace();
}
return convertView;
}
接下就是為這個ListView設定資料源Adapter,在賬号選擇視窗顯示的時候進行設定,添加到id為iconSelectBtn的ImageButton的OnClickListener中代碼如下:
view plaincopy to clipboardprint?
ImageButton iconSelectBtn=(ImageButton)findViewById(R.id.iconSelectBtn);
iconSelectBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
......
dialog.show();
UserAdapater adapater = new UserAdapater();
ListView listview=(ListView)diaView.findViewById(R.id.list);
listview.setVerticalScrollBarEnabled(false);// ListView去掉下拉條
listview.setAdapter(adapater);
listview.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> arg0, View view,int arg2, long arg3) {
TextView tv=(TextView)view.findViewById(R.id.showName);
iconSelect.setText(tv.getText());
ImageView iv=(ImageView)view.findViewById(R.id.iconImg);
icon.setImageDrawable(iv.getDrawable());
dialog.dismiss();
}
});
}
});
ImageButton iconSelectBtn=(ImageButton)findViewById(R.id.iconSelectBtn);
iconSelectBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
......
dialog.show();
UserAdapater adapater = new UserAdapater();
ListView listview=(ListView)diaView.findViewById(R.id.list);
listview.setVerticalScrollBarEnabled(false);// ListView去掉下拉條
listview.setAdapter(adapater);
listview.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> arg0, View view,int arg2, long arg3) {
TextView tv=(TextView)view.findViewById(R.id.showName);
iconSelect.setText(tv.getText());
ImageView iv=(ImageView)view.findViewById(R.id.iconImg);
icon.setImageDrawable(iv.getDrawable());
dialog.dismiss();
}
});
}
});
通過上面代碼完成了賬号選擇的功能,接下來給id為login的ImageButton添加OnClickListener,使得點選後以目前選擇賬号進入微網誌首頁,代碼如下:
view plaincopy to clipboardprint?
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
......
ImageButton login=(ImageButton)findViewById(R.id.login);
login.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
GoHome();
}
});
}
//進入使用者首頁
private void GoHome(){
if(userList!=null)
{
String name=iconSelect.getText().toString();
UserInfo u=GetUserByName(name);
if(u!=null)
{
ConfigHelper.nowUser=u;//擷取目前選擇的使用者并且儲存
}
}
if(ConfigHelper.nowUser!=null)
{
//進入使用者首頁
Intent intent = new Intent();
intent.setClass(LoginActivity.this, HomeActivity.class);
startActivity(intent);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
......
ImageButton login=(ImageButton)findViewById(R.id.login);
login.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
GoHome();
}
});
}
//進入使用者首頁
private void GoHome(){
if(userList!=null)
{
String name=iconSelect.getText().toString();
UserInfo u=GetUserByName(name);
if(u!=null)
{
ConfigHelper.nowUser=u;//擷取目前選擇的使用者并且儲存
}
}
if(ConfigHelper.nowUser!=null)
{
//進入使用者首頁
Intent intent = new Intent();
intent.setClass(LoginActivity.this, HomeActivity.class);
startActivity(intent);
}
}
在上面的GoHome方法中ConfigHelper.nowUser是類型為UserInfo的static類型用來儲存目前登入賬号的資訊,替代web中session使用。
最後添加如下方法,用來當這個登入LoginActivity結束的時候儲存目前選擇的賬戶名稱到SharedPreferences中,以便幫使用者記住登入賬号的功能,就是前面的initUser() 初始賬号的方法中會擷取儲存在SharedPreferences中的賬戶名稱,代碼如下:
view plaincopy to clipboardprint?
@Override
protected void onStop() {
//獲得SharedPreferences對象
SharedPreferences MyPreferences = getSharedPreferences(Select_Name, Activity.MODE_PRIVATE);
//獲得SharedPreferences.Editor對象
SharedPreferences.Editor editor = MyPreferences.edit();
//儲存元件中的值
editor.putString("name", iconSelect.getText().toString());
editor.commit();
super.onStop();
}
@Override
protected void onStop() {
//獲得SharedPreferences對象
SharedPreferences MyPreferences = getSharedPreferences(Select_Name, Activity.MODE_PRIVATE);
//獲得SharedPreferences.Editor對象
SharedPreferences.Editor editor = MyPreferences.edit();
//儲存元件中的值
editor.putString("name", iconSelect.getText().toString());
editor.commit();
super.onStop();
}
至此登入頁面功能篇結束,請繼續關注下一篇。
android開發我的新浪微部落格戶端-使用者首頁面UI篇(5.1)
6 天前 上傳
下載下傳附件 (101.43 KB) 在前篇完成了使用者登入功能後開始使用者首頁的開發,使用者的首頁主要的内容是目前登入使用者關注的微網誌清單,本篇先來講講UI的實作,效果如上圖,整個頁面分為上、中、下三部分,上面部分是工具條,顯示目前登入使用者的昵稱以及寫微網誌、重新整理兩個功能按鈕;中間部分是目前使用者關注的最新微網誌清單,下面部分是功能切換欄,用來進行各個功能之間的切換。
首先建立名為HomeActivity.java的Activity作為使用者首頁,然後在res/layout目錄下建立名為home.xml的Layout,具體代碼如下:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="3px">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/logo_ss">
</ImageView>
<TextView
android:id="@+id/showName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="#343434"
android:textSize="15px">
</TextView>
<ImageButton
android:id="@+id/writeBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/refreshBtn"
android:background="@drawable/btn_write_selector">
</ImageButton>
<ImageButton
android:id="@+id/refreshBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="12px"
android:background="@drawable/btn_refresh_selector">
</ImageButton>
</RelativeLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/hr">
</LinearLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:id="@+id/Msglist"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:divider="@drawable/divider"
android:dividerHeight="2px"
android:layout_margin="0px"
android:background="#BBFFFFFF"
android:cacheColorHint="#00000000"
android:layout_above="@+id/toolbarLayout"
android:fastScrollEnabled="true"
android:focusable="true">
</ListView>
<LinearLayout
android:id="@+id/loadingLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="invisible"
android:layout_centerInParent="true">
<ProgressBar
android:id="@+id/loading"
android:layout_width="31px"
android:layout_height="31px"
android:layout_gravity="center"
style="@style/progressStyle">
</ProgressBar>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在載入"
android:textSize="12px"
android:textColor="#9c9c9c"
android:layout_gravity="center"
android:layout_below="@+id/loading">
</TextView>
</LinearLayout>
<LinearLayout
android:id="@+id/toolbarLayout"
android:layout_width="fill_parent"
android:layout_height="44dip"
android:layout_alignParentBottom="true">
</LinearLayout>
</RelativeLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="3px">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/logo_ss">
</ImageView>
<TextView
android:id="@+id/showName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="#343434"
android:textSize="15px">
</TextView>
<ImageButton
android:id="@+id/writeBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/refreshBtn"
android:background="@drawable/btn_write_selector">
</ImageButton>
<ImageButton
android:id="@+id/refreshBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="12px"
android:background="@drawable/btn_refresh_selector">
</ImageButton>
</RelativeLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/hr">
</LinearLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:id="@+id/Msglist"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:divider="@drawable/divider"
android:dividerHeight="2px"
android:layout_margin="0px"
android:background="#BBFFFFFF"
android:cacheColorHint="#00000000"
android:layout_above="@+id/toolbarLayout"
android:fastScrollEnabled="true"
android:focusable="true">
</ListView>
<LinearLayout
android:id="@+id/loadingLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="invisible"
android:layout_centerInParent="true">
<ProgressBar
android:id="@+id/loading"
android:layout_width="31px"
android:layout_height="31px"
android:layout_gravity="center"
style="@style/progressStyle">
</ProgressBar>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在載入"
android:textSize="12px"
android:textColor="#9c9c9c"
android:layout_gravity="center"
android:layout_below="@+id/loading">
</TextView>
</LinearLayout>
<LinearLayout
android:id="@+id/toolbarLayout"
android:layout_width="fill_parent"
android:layout_height="44dip"
android:layout_alignParentBottom="true">
</LinearLayout>
</RelativeLayout>
</LinearLayout>
這個布局首先是一個豎直的根LinearLayout,在這個根LinearLayout裡面分别是兩個RelativeLayout, 第一個RelativeLayout 用來顯示頁面的工具條,第二個RelativeLayout用來顯示清單以及底部的功能欄,特别主要在這第二個RelativeLayout中有一個id為loadingLayout的LinearLayout是用來顯示資料載入中的動畫,它的android:visibility屬性為invisible(也可以設定成gone,差別:invisible這個View在ViewGroupt中仍保留它的位置,不重新layout
gone>不可見,但這個View在ViewGroupt中不保留位置,重新layout,那後面的view就會取代他的位置。 ),也就是一開始不顯示的意思,接下來看看
<ProgressBar
android:id="@+id/loading"
android:layout_width="31px"
android:layout_height="31px"
android:layout_gravity="center"
style="@style/progressStyle">
</ProgressBar>
這個ProgressBar控件就是用來顯示動畫用的,關鍵就是 style="@style/progressStyle",在res/values目錄下建立名為loadingstyles.xml,内容如下:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<mce:style name="progressStyle" width="38" height="38" parent="@android:style/Widget.ProgressBar.Small"><!--
<item name="android:indeterminateDrawable">@anim/loading</item>
--></mce:style><style name="progressStyle" width="38" height="38" parent="@android:style/Widget.ProgressBar.Small" mce_bogus="1"><item name="android:indeterminateDrawable">@anim/loading</item></style>
</resources>
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<mce:style name="progressStyle" width="38" height="38" parent="@android:style/Widget.ProgressBar.Small"><!--
<item name="android:indeterminateDrawable">@anim/loading</item>
--></mce:style><style name="progressStyle" width="38" height="38" parent="@android:style/Widget.ProgressBar.Small" mce_bogus="1"><item name="android:indeterminateDrawable">@anim/loading</item></style>
</resources>
接着準備好r1.png - r8.png,
6 天前 上傳
下載下傳附件 (998 Bytes)
6 天前 上傳
下載下傳附件 (980 Bytes)
6 天前 上傳
下載下傳附件 (1013 Bytes)
6 天前 上傳
下載下傳附件 (1014 Bytes)
6 天前 上傳
下載下傳附件 (986 Bytes)
6 天前 上傳
下載下傳附件 (992 Bytes)
6 天前 上傳
下載下傳附件 (1010 Bytes)
6 天前 上傳
下載下傳附件 (1 KB) 八張不同的小圖檔分别代表每旋轉45度圖檔,八張剛好是360度。把這些圖檔添加到res/drawable-mdpi目錄中。然後在res/anim目錄下建立名為loading.xml動畫檔案,内容如下:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="UTF-8"?>
<animation-list android:oneshot="false"
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:duration="200" android:drawable="@drawable/r1" />
<item android:duration="200" android:drawable="@drawable/r2" />
<item android:duration="200" android:drawable="@drawable/r3" />
<item android:duration="200" android:drawable="@drawable/r4" />
<item android:duration="200" android:drawable="@drawable/r5" />
<item android:duration="200" android:drawable="@drawable/r6" />
<item android:duration="200" android:drawable="@drawable/r7" />
<item android:duration="200" android:drawable="@drawable/r8" />
</animation-list>
<?xml version="1.0" encoding="UTF-8"?>
<animation-list android:oneshot="false"
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:duration="200" android:drawable="@drawable/r1" />
<item android:duration="200" android:drawable="@drawable/r2" />
<item android:duration="200" android:drawable="@drawable/r3" />
<item android:duration="200" android:drawable="@drawable/r4" />
<item android:duration="200" android:drawable="@drawable/r5" />
<item android:duration="200" android:drawable="@drawable/r6" />
<item android:duration="200" android:drawable="@drawable/r7" />
<item android:duration="200" android:drawable="@drawable/r8" />
</animation-list>
關于Android播放動畫實作我是參考http://www.eoeandroid.com/forum.php?mod=viewthread&tid=67311&extra=
本篇到這裡就結束了,下一篇繼續講使用者首頁的功能實作,請關注。
android開發我的新浪微部落格戶端-使用者首頁面功能篇(5.2)
6 天前 上傳
下載下傳附件 (101.43 KB)
上一篇完成使用者首頁的UI實作,本篇接下來講功能部分的實作,本頁面主要的功能就使用者關注的最新微網誌清單,從上一篇中知道本清單是用ID為Msglist的ListView控件來實作,本篇的主要就講解如果擷取微網誌清單資料給這個ListView提供顯示資料。ListView每一條子資料分别由使用者頭像、使用者昵稱、釋出時間、是否包含照片、微網誌内容這五部分組成,根據這五部分定義一個名為WeiBoInfo.java實體類,代碼如下:
view plaincopy to clipboardprint?
public class WeiBoInfo {
//文章id
private String id;
public String getId(){
return id;
}
public void setId(String id){
this.id=id;
}
//釋出人id
private String userId;
public String getUserId(){
return userId;
}
public void setUserId(String userId){
this.userId=userId;
}
//釋出人名字
private String userName;
public String getUserName(){
return userName;
}
public void setUserName(String userName){
this.userName=userName;
}
//釋出人頭像
private String userIcon;
public String getUserIcon(){
return userIcon;
}
public void setUserIcon(String userIcon){
this.userIcon=userIcon;
}
//釋出時間
private String time;
public String getTime(){
return time;
}
public void setTime(String time)
{
this.time=time;
}
//是否有圖檔
private Boolean haveImage=false;
public Boolean getHaveImage(){
return haveImage;
}
public void setHaveImage(Boolean haveImage){
this.haveImage=haveImage;
}
//文章内容
private String text;
public String getText(){
return text;
}
public void setText(String text){
this.text=text;
}
}
public class WeiBoInfo {
//文章id
private String id;
public String getId(){
return id;
}
public void setId(String id){
this.id=id;
}
//釋出人id
private String userId;
public String getUserId(){
return userId;
}
public void setUserId(String userId){
this.userId=userId;
}
//釋出人名字
private String userName;
public String getUserName(){
return userName;
}
public void setUserName(String userName){
this.userName=userName;
}
//釋出人頭像
private String userIcon;
public String getUserIcon(){
return userIcon;
}
public void setUserIcon(String userIcon){
this.userIcon=userIcon;
}
//釋出時間
private String time;
public String getTime(){
return time;
}
public void setTime(String time)
{
this.time=time;
}
//是否有圖檔
private Boolean haveImage=false;
public Boolean getHaveImage(){
return haveImage;
}
public void setHaveImage(Boolean haveImage){
this.haveImage=haveImage;
}
//文章内容
private String text;
public String getText(){
return text;
}
public void setText(String text){
this.text=text;
}
}
然後在res/layout目錄下建立名為weibo.xml的Layout用來控制ListView子項的顯示部件,代碼很簡單不多解釋了,直接看下面代碼:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/wbicon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/usericon"
android:layout_margin="8px">
</ImageView>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="0px"
android:paddingRight="5px"
android:layout_marginTop="5px"
android:layout_marginBottom="5px">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/wbuser"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15px"
android:textColor="#424952"
android:layout_alignParentLeft="true">
</TextView>
<ImageView
android:id="@+id/wbimage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3px"
android:layout_marginRight="5px"
android:layout_toLeftOf="@+id/wbtime">
</ImageView>
<TextView
android:id="@+id/wbtime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:textColor="#f7a200"
android:textSize="12px">
</TextView>
</RelativeLayout>
<TextView
android:id="@+id/wbtext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#424952"
android:textSize="13px"
android:layout_marginTop="4px">
</TextView>
</LinearLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/wbicon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/usericon"
android:layout_margin="8px">
</ImageView>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="0px"
android:paddingRight="5px"
android:layout_marginTop="5px"
android:layout_marginBottom="5px">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/wbuser"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15px"
android:textColor="#424952"
android:layout_alignParentLeft="true">
</TextView>
<ImageView
android:id="@+id/wbimage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3px"
android:layout_marginRight="5px"
android:layout_toLeftOf="@+id/wbtime">
</ImageView>
<TextView
android:id="@+id/wbtime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:textColor="#f7a200"
android:textSize="12px">
</TextView>
</RelativeLayout>
<TextView
android:id="@+id/wbtext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#424952"
android:textSize="13px"
android:layout_marginTop="4px">
</TextView>
</LinearLayout>
</LinearLayout>
接下來為清單控件定義一個資料Adapter,代碼如下:
view plaincopy to clipboardprint?
private List<WeiBoInfo> wbList;
//微網誌清單Adapater
public class WeiBoAdapater extends BaseAdapter{
private AsyncImageLoader asyncImageLoader;
@Override
public int getCount() {
return wbList.size();
}
@Override
public Object getItem(int position) {
return wbList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
asyncImageLoader = new AsyncImageLoader();
convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.weibo, null);
WeiBoHolder wh = new WeiBoHolder();
wh.wbicon = (ImageView) convertView.findViewById(R.id.wbicon);
wh.wbtext = (TextView) convertView.findViewById(R.id.wbtext);
wh.wbtime = (TextView) convertView.findViewById(R.id.wbtime);
wh.wbuser = (TextView) convertView.findViewById(R.id.wbuser);
wh.wbimage=(ImageView) convertView.findViewById(R.id.wbimage);
WeiBoInfo wb = wbList.get(position);
if(wb!=null){
convertView.setTag(wb.getId());
wh.wbuser.setText(wb.getUserName());
wh.wbtime.setText(wb.getTime());
wh.wbtext.setText(wb.getText(), TextView.BufferType.SPANNABLE);
textHighlight(wh.wbtext,new char[]{'#'},new char[]{'#'});
textHighlight(wh.wbtext,new char[]{'@'},new char[]{':',' '});
textHighlight2(wh.wbtext,"http://"," ");
if(wb.getHaveImage()){
wh.wbimage.setImageResource(R.drawable.images);
}
Drawable cachedImage = asyncImageLoader.loadDrawable(wb.getUserIcon(),wh.wbicon, new ImageCallback(){
@Override
public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl) {
imageView.setImageDrawable(imageDrawable);
}
});
if (cachedImage == null) {
wh.wbicon.setImageResource(R.drawable.usericon);
}else{
wh.wbicon.setImageDrawable(cachedImage);
}
}
return convertView;
}
private List<WeiBoInfo> wbList;
//微網誌清單Adapater
public class WeiBoAdapater extends BaseAdapter{
private AsyncImageLoader asyncImageLoader;
@Override
public int getCount() {
return wbList.size();
}
@Override
public Object getItem(int position) {
return wbList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
asyncImageLoader = new AsyncImageLoader();
convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.weibo, null);
WeiBoHolder wh = new WeiBoHolder();
wh.wbicon = (ImageView) convertView.findViewById(R.id.wbicon);
wh.wbtext = (TextView) convertView.findViewById(R.id.wbtext);
wh.wbtime = (TextView) convertView.findViewById(R.id.wbtime);
wh.wbuser = (TextView) convertView.findViewById(R.id.wbuser);
wh.wbimage=(ImageView) convertView.findViewById(R.id.wbimage);
WeiBoInfo wb = wbList.get(position);
if(wb!=null){
convertView.setTag(wb.getId());
wh.wbuser.setText(wb.getUserName());
wh.wbtime.setText(wb.getTime());
wh.wbtext.setText(wb.getText(), TextView.BufferType.SPANNABLE);
textHighlight(wh.wbtext,new char[]{'#'},new char[]{'#'});
textHighlight(wh.wbtext,new char[]{'@'},new char[]{':',' '});
textHighlight2(wh.wbtext,"http://"," ");
if(wb.getHaveImage()){
wh.wbimage.setImageResource(R.drawable.images);
}
Drawable cachedImage = asyncImageLoader.loadDrawable(wb.getUserIcon(),wh.wbicon, new ImageCallback(){
@Override
public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl) {
imageView.setImageDrawable(imageDrawable);
}
});
if (cachedImage == null) {
wh.wbicon.setImageResource(R.drawable.usericon);
}else{
wh.wbicon.setImageDrawable(cachedImage);
}
}
return convertView;
}
上面的這個Adapter實作沒有什麼特别的很普通,不過這個中使用了AsyncImageLoader的方法,這個是用來實作使用者頭像圖示的異步載入顯示,這樣能提高清單顯示的速度,提高使用者體驗,AsyncImageLoader的代碼如下:
view plaincopy to clipboardprint?
public class AsyncImageLoader {
//SoftReference是軟引用,是為了更好的為了系統回收變量
private HashMap<String, SoftReference<Drawable>> imageCache;
public AsyncImageLoader() {
imageCache = new HashMap<String, SoftReference<Drawable>>();
}
public Drawable loadDrawable(final String imageUrl,final ImageView imageView, final ImageCallback imageCallback){
if (imageCache.containsKey(imageUrl)) {
//從緩存中擷取
SoftReference<Drawable> softReference = imageCache.get(imageUrl);
Drawable drawable = softReference.get();
if (drawable != null) {
return drawable;
}
}
final Handler handler = new Handler() {
public void handleMessage(Message message) {
imageCallback.imageLoaded((Drawable) message.obj, imageView,imageUrl);
}
};
//建立新一個新的線程下載下傳圖檔
new Thread() {
@Override
public void run() {
Drawable drawable = loadImageFromUrl(imageUrl);
imageCache.put(imageUrl, new SoftReference<Drawable>(drawable));
Message message = handler.obtainMessage(0, drawable);
handler.sendMessage(message);
}
}.start();
return null;
}
public static Drawable loadImageFromUrl(String url){
URL m;
InputStream i = null;
try {
m = new URL(url);
i = (InputStream) m.getContent();
} catch (MalformedURLException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Drawable d = Drawable.createFromStream(i, "src");
return d;
}
//回調接口
public interface ImageCallback {
public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl);
}
}
public class AsyncImageLoader {
//SoftReference是軟引用,是為了更好的為了系統回收變量
private HashMap<String, SoftReference<Drawable>> imageCache;
public AsyncImageLoader() {
imageCache = new HashMap<String, SoftReference<Drawable>>();
}
public Drawable loadDrawable(final String imageUrl,final ImageView imageView, final ImageCallback imageCallback){
if (imageCache.containsKey(imageUrl)) {
//從緩存中擷取
SoftReference<Drawable> softReference = imageCache.get(imageUrl);
Drawable drawable = softReference.get();
if (drawable != null) {
return drawable;
}
}
final Handler handler = new Handler() {
public void handleMessage(Message message) {
imageCallback.imageLoaded((Drawable) message.obj, imageView,imageUrl);
}
};
//建立新一個新的線程下載下傳圖檔
new Thread() {
@Override
public void run() {
Drawable drawable = loadImageFromUrl(imageUrl);
imageCache.put(imageUrl, new SoftReference<Drawable>(drawable));
Message message = handler.obtainMessage(0, drawable);
handler.sendMessage(message);
}
}.start();
return null;
}
public static Drawable loadImageFromUrl(String url){
URL m;
InputStream i = null;
try {
m = new URL(url);
i = (InputStream) m.getContent();
} catch (MalformedURLException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Drawable d = Drawable.createFromStream(i, "src");
return d;
}
//回調接口
public interface ImageCallback {
public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl);
}
}
完成上述的工作後,接下來就是顯示微薄清單, 在HomeActivity的onCreate方法中調用loadList();代碼如下:
view plaincopy to clipboardprint?
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home);
。。。。。。
loadList();
}
private void loadList(){
if(ConfigHelper.nowUser==null)
{
}
else
{
user=ConfigHelper.nowUser;
//顯示目前使用者名稱
TextView showName=(TextView)findViewById(R.id.showName);
showName.setText(user.getUserName());
OAuth auth=new OAuth();
String url = "http://api.t.sina.com.cn/statuses/friends_timeline.json";
List params=new ArrayList();
params.add(new BasicNameValuePair("source", auth.consumerKey));
HttpResponse response =auth.SignRequest(user.getToken(), user.getTokenSecret(), url, params);
if (200 == response.getStatusLine().getStatusCode()){
try {
InputStream is = response.getEntity().getContent();
Reader reader = new BufferedReader(new InputStreamReader(is), 4000);
StringBuilder buffer = new StringBuilder((int) response.getEntity().getContentLength());
try {
char[] tmp = new char[1024];
int l;
while ((l = reader.read(tmp)) != -1) {
buffer.append(tmp, 0, l);
}
} finally {
reader.close();
}
String string = buffer.toString();
//Log.e("json", "rs:" + string);
response.getEntity().consumeContent();
JSONArray data=new JSONArray(string);
for(int i=0;i<data.length();i++)
{
JSONObject d=data.getJSONObject(i);
//Log.e("json", "rs:" + d.getString("created_at"));
if(d!=null){
JSONObject u=d.getJSONObject("user");
if(d.has("retweeted_status")){
JSONObject r=d.getJSONObject("retweeted_status");
}
//微網誌id
String id=d.getString("id");
String userId=u.getString("id");
String userName=u.getString("screen_name");
String userIcon=u.getString("profile_image_url");
Log.e("userIcon", userIcon);
String time=d.getString("created_at");
String text=d.getString("text");
Boolean haveImg=false;
if(d.has("thumbnail_pic")){
haveImg=true;
//String thumbnail_pic=d.getString("thumbnail_pic");
//Log.e("thumbnail_pic", thumbnail_pic);
}
Date date=new Date(time);
time=ConvertTime(date);
if(wbList==null){
wbList=new ArrayList<WeiBoInfo>();
}
WeiBoInfo w=new WeiBoInfo();
w.setId(id);
w.setUserId(userId);
w.setUserName(userName);
w.setTime(time);
w.setText(text);
w.setHaveImage(haveImg);
w.setUserIcon(userIcon);
wbList.add(w);
}
}
}catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
if(wbList!=null)
{
WeiBoAdapater adapater = new WeiBoAdapater();
ListView Msglist=(ListView)findViewById(R.id.Msglist);
Msglist.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> arg0, View view,int arg2, long arg3) {
Object obj=view.getTag();
if(obj!=null){
String id=obj.toString();
Intent intent = new Intent(HomeActivity.this,ViewActivity.class);
Bundle b=new Bundle();
b.putString("key", id);
intent.putExtras(b);
startActivity(intent);
}
}
});
Msglist.setAdapter(adapater);
}
}
loadingLayout.setVisibility(View.GONE);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home);
。。。。。。
loadList();
}
private void loadList(){
if(ConfigHelper.nowUser==null)
{
}
else
{
user=ConfigHelper.nowUser;
//顯示目前使用者名稱
TextView showName=(TextView)findViewById(R.id.showName);
showName.setText(user.getUserName());
OAuth auth=new OAuth();
String url = "http://api.t.sina.com.cn/statuses/friends_timeline.json";
List params=new ArrayList();
params.add(new BasicNameValuePair("source", auth.consumerKey));
HttpResponse response =auth.SignRequest(user.getToken(), user.getTokenSecret(), url, params);
if (200 == response.getStatusLine().getStatusCode()){
try {
InputStream is = response.getEntity().getContent();
Reader reader = new BufferedReader(new InputStreamReader(is), 4000);
StringBuilder buffer = new StringBuilder((int) response.getEntity().getContentLength());
try {
char[] tmp = new char[1024];
int l;
while ((l = reader.read(tmp)) != -1) {
buffer.append(tmp, 0, l);
}
} finally {
reader.close();
}
String string = buffer.toString();
//Log.e("json", "rs:" + string);
response.getEntity().consumeContent();
JSONArray data=new JSONArray(string);
for(int i=0;i<data.length();i++)
{
JSONObject d=data.getJSONObject(i);
//Log.e("json", "rs:" + d.getString("created_at"));
if(d!=null){
JSONObject u=d.getJSONObject("user");
if(d.has("retweeted_status")){
JSONObject r=d.getJSONObject("retweeted_status");
}
//微網誌id
String id=d.getString("id");
String userId=u.getString("id");
String userName=u.getString("screen_name");
String userIcon=u.getString("profile_image_url");
Log.e("userIcon", userIcon);
String time=d.getString("created_at");
String text=d.getString("text");
Boolean haveImg=false;
if(d.has("thumbnail_pic")){
haveImg=true;
//String thumbnail_pic=d.getString("thumbnail_pic");
//Log.e("thumbnail_pic", thumbnail_pic);
}
Date date=new Date(time);
time=ConvertTime(date);
if(wbList==null){
wbList=new ArrayList<WeiBoInfo>();
}
WeiBoInfo w=new WeiBoInfo();
w.setId(id);
w.setUserId(userId);
w.setUserName(userName);
w.setTime(time);
w.setText(text);
w.setHaveImage(haveImg);
w.setUserIcon(userIcon);
wbList.add(w);
}
}
}catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
if(wbList!=null)
{
WeiBoAdapater adapater = new WeiBoAdapater();
ListView Msglist=(ListView)findViewById(R.id.Msglist);
Msglist.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> arg0, View view,int arg2, long arg3) {
Object obj=view.getTag();
if(obj!=null){
String id=obj.toString();
Intent intent = new Intent(HomeActivity.this,ViewActivity.class);
Bundle b=new Bundle();
b.putString("key", id);
intent.putExtras(b);
startActivity(intent);
}
}
});
Msglist.setAdapter(adapater);
}
}
loadingLayout.setVisibility(View.GONE);
}
上面的loadList() 方法通過新浪Api接口http://api.t.sina.com.cn/statuses/friends_timeline.json擷取目前登入使用者及其所關注使用者的最新微網誌消息,然後顯示到清單中。
這樣就完成了使用者首頁功能的開發。
android開發我的新浪微部落格戶端-閱讀微網誌UI篇(6.1)
6 天前 上傳
下載下傳附件 (154.98 KB) 上一篇完成了微網誌清單的功能,本篇接着做預讀微網誌的功能,本篇主要講講UI部分的實作,最終實作的效果如上圖所示。整個顯示頁面從上往下分為四部分,第一部分頂部工具條、第二部分作者頭像和名稱、第三部分微網誌正文、第四部分功能按鈕區。建立名為ViewActivity.java作為閱讀微網誌的頁面,再res/layout目錄下建立名為view.xml的Layout,代碼如下:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="3px">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/logo_ss">
</ImageView>
<TextView
android:id="@+id/showName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="#343434"
android:text="閱讀微網誌"
android:textSize="16px">
</TextView>
<ImageButton
android:id="@+id/returnBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/homeBtn"
android:background="@drawable/bnt_return_selector">
</ImageButton>
<ImageButton
android:id="@+id/homeBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="12px"
android:background="@drawable/btn_home_selector">
</ImageButton>
</RelativeLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/hr">
</LinearLayout>
<RelativeLayout
android:id="@+id/user_bg"
android:layout_width="fill_parent"
android:layout_height="78px"
android:paddingTop="8px"
android:paddingLeft="15px"
android:background="@drawable/u_bg_v">
<ImageView
android:id="@+id/user_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:src="@drawable/usericon">
</ImageView>
<TextView
android:id="@+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/user_icon"
android:layout_marginLeft="10px"
android:layout_marginTop="18px"
android:textColor="#000000">
</TextView>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="5px"
android:layout_marginTop="10px"
android:src="@drawable/sjjt">
</ImageView>
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="17px"
android:paddingRight="17px"
android:paddingBottom="5px"
android:layout_above="@+id/menu_layout">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="15px">
</TextView>
<ImageView
android:id="@+id/pic"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</ImageView>
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="@+id/loadingLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
android:layout_centerInParent="true">
<ProgressBar
android:id="@+id/loading"
android:layout_width="31px"
android:layout_height="31px"
android:layout_gravity="center"
style="@style/progressStyle">
</ProgressBar>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在載入"
android:textSize="12px"
android:textColor="#9c9c9c"
android:layout_gravity="center"
android:layout_below="@+id/loading">
</TextView>
</LinearLayout>
<TableLayout
android:id="@+id/menu_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_alignParentBottom="true"
android:layout_marginBottom="5px">
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center">
<Button
android:id="@+id/btn_gz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#3882b8"
android:textSize="15px"
android:text=" 關注(1231)"
android:background="@drawable/lt_selector">
</Button>
<Button
android:id="@+id/btn_pl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#3882b8"
android:textSize="15px"
android:text=" 評論(31)"
android:background="@drawable/rt_selector">
</Button>
</TableRow>
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#3882b8"
android:textSize="15px"
android:layout_gravity="left"
android:text="重新整理"
android:background="@drawable/lb_selector">
</Button>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#3882b8"
android:textSize="15px"
android:text="收藏"
android:background="@drawable/rb_selector">
</Button>
</TableRow>
</TableLayout>
</RelativeLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="3px">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/logo_ss">
</ImageView>
<TextView
android:id="@+id/showName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="#343434"
android:text="閱讀微網誌"
android:textSize="16px">
</TextView>
<ImageButton
android:id="@+id/returnBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/homeBtn"
android:background="@drawable/bnt_return_selector">
</ImageButton>
<ImageButton
android:id="@+id/homeBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="12px"
android:background="@drawable/btn_home_selector">
</ImageButton>
</RelativeLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/hr">
</LinearLayout>
<RelativeLayout
android:id="@+id/user_bg"
android:layout_width="fill_parent"
android:layout_height="78px"
android:paddingTop="8px"
android:paddingLeft="15px"
android:background="@drawable/u_bg_v">
<ImageView
android:id="@+id/user_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:src="@drawable/usericon">
</ImageView>
<TextView
android:id="@+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/user_icon"
android:layout_marginLeft="10px"
android:layout_marginTop="18px"
android:textColor="#000000">
</TextView>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="5px"
android:layout_marginTop="10px"
android:src="@drawable/sjjt">
</ImageView>
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="17px"
android:paddingRight="17px"
android:paddingBottom="5px"
android:layout_above="@+id/menu_layout">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="15px">
</TextView>
<ImageView
android:id="@+id/pic"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</ImageView>
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="@+id/loadingLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
android:layout_centerInParent="true">
<ProgressBar
android:id="@+id/loading"
android:layout_width="31px"
android:layout_height="31px"
android:layout_gravity="center"
style="@style/progressStyle">
</ProgressBar>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在載入"
android:textSize="12px"
android:textColor="#9c9c9c"
android:layout_gravity="center"
android:layout_below="@+id/loading">
</TextView>
</LinearLayout>
<TableLayout
android:id="@+id/menu_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_alignParentBottom="true"
android:layout_marginBottom="5px">
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center">
<Button
android:id="@+id/btn_gz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#3882b8"
android:textSize="15px"
android:text=" 關注(1231)"
android:background="@drawable/lt_selector">
</Button>
<Button
android:id="@+id/btn_pl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#3882b8"
android:textSize="15px"
android:text=" 評論(31)"
android:background="@drawable/rt_selector">
</Button>
</TableRow>
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#3882b8"
android:textSize="15px"
android:layout_gravity="left"
android:text="重新整理"
android:background="@drawable/lb_selector">
</Button>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#3882b8"
android:textSize="15px"
android:text="收藏"
android:background="@drawable/rb_selector">
</Button>
</TableRow>
</TableLayout>
</RelativeLayout>
</LinearLayout>
上面這個布局實作起來并不複雜, 主要看看功能按鈕區的4個按鈕的點選上去的切換背景的效果,以關注按鈕為例子看這行設定,android:background="@drawable/lt_selector",在res/drawable-mdpi目錄下建立名為lt_selector.xml用來實作點選上去切換圖檔的效果,具體代碼如下:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/tbtn_1" />
<item android:state_pressed="true" android:drawable="@drawable/tbtn_h_1" />
</selector>
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/tbtn_1" />
<item android:state_pressed="true" android:drawable="@drawable/tbtn_h_1" />
</selector>
本篇雖然看layout檔案非常的長,其實仔細看看非常的簡單了沒有什麼難和複雜的了,就是按照前面的經驗控制好圖檔以及控件的顯示位置和樣式即可,本篇中用了一個ScrollView控件這個是前面沒有用到過的,主要是用來當微網誌的内容超出顯示區域的時候出現滾動條用的這個非常容易使用,是以就簡單寫一下到此結束了,請繼續關注下一篇閱讀微網誌的功能篇。
android開發我的新浪微部落格戶端-閱讀微網誌功能篇(6.2)
6 天前 上傳
下載下傳附件 (154.98 KB) 注:最近由于OAuth上傳圖檔碰到了難題,一直在做這方面的研究導緻部落格很久沒有更新。
在上面一篇中已經實作了預讀微網誌的UI界面,效果如上圖,接下來完成功能部分的代碼,當使用者在上一個清單界面的清單中點選某一條微網誌的時候顯示這個閱讀微網誌的界面,在這個界面中根據傳來的微網誌ID,然後根據這個ID通過api擷取微網誌的具體内容進行顯示。
在ViewActivity.class的onCreate方法中添加如下代碼:
view plaincopy to clipboardprint?
private UserInfo user;
private String key="";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view);
。。。。。
//擷取上一個頁面傳遞過來的key,key為某一條微網誌的id
Intent i=this.getIntent();
if(!i.equals(null)){
Bundle b=i.getExtras();
if(b!=null){
if(b.containsKey("key")){
key = b.getString("key");
view(key);
}
}
}
}
private UserInfo user;
private String key="";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view);
。。。。。
//擷取上一個頁面傳遞過來的key,key為某一條微網誌的id
Intent i=this.getIntent();
if(!i.equals(null)){
Bundle b=i.getExtras();
if(b!=null){
if(b.containsKey("key")){
key = b.getString("key");
view(key);
}
}
}
}
接下來就是view方法具體擷取微網誌内容的方法,在這個方法中如果擷取的本條微網誌如果包含圖檔那麼就用前面AsyncImageLoader的方法異步載入圖檔并且進行顯示,同時在這個方法中還要擷取本條微網誌被轉發的次數以及評論的次數,具體代碼如下:
view plaincopy to clipboardprint?
private void view(String id){
user=ConfigHelper.nowUser;
OAuth auth=new OAuth();
String url = "http://api.t.sina.com.cn/statuses/show/:id.json";
List params=new ArrayList();
params.add(new BasicNameValuePair("source", auth.consumerKey));
params.add(new BasicNameValuePair("id", id));
HttpResponse response =auth.SignRequest(user.getToken(), user.getTokenSecret(), url, params);
if (200 == response.getStatusLine().getStatusCode()){
try {
InputStream is = response.getEntity().getContent();
Reader reader = new BufferedReader(new InputStreamReader(is), 4000);
StringBuilder buffer = new StringBuilder((int) response.getEntity().getContentLength());
try {
char[] tmp = new char[1024];
int l;
while ((l = reader.read(tmp)) != -1) {
buffer.append(tmp, 0, l);
}
} finally {
reader.close();
}
String string = buffer.toString();
//Log.e("json", "rs:" + string);
response.getEntity().consumeContent();
JSONObject data=new JSONObject(string);
if(data!=null){
JSONObject u=data.getJSONObject("user");
String userName=u.getString("screen_name");
String userIcon=u.getString("profile_image_url");
Log.e("userIcon", userIcon);
String time=data.getString("created_at");
String text=data.getString("text");
TextView utv=(TextView)findViewById(R.id.user_name);
utv.setText(userName);
TextView ttv=(TextView)findViewById(R.id.text);
ttv.setText(text);
ImageView iv=(ImageView)findViewById(R.id.user_icon);
AsyncImageLoader asyncImageLoader = new AsyncImageLoader();
Drawable cachedImage = asyncImageLoader.loadDrawable(userIcon,iv, new ImageCallback(){
@Override
public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl) {
imageView.setImageDrawable(imageDrawable);
}
});
if (cachedImage == null)
{
iv.setImageResource(R.drawable.usericon);
}
else
{
iv.setImageDrawable(cachedImage);
}
if(data.has("bmiddle_pic")){
String picurl=data.getString("bmiddle_pic");
String picurl2=data.getString("original_pic");
ImageView pic=(ImageView)findViewById(R.id.pic);
pic.setTag(picurl2);
pic.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Object obj=v.getTag();
Intent intent = new Intent(ViewActivity.this,ImageActivity.class);
Bundle b=new Bundle();
b.putString("url", obj.toString());
intent.putExtras(b);
startActivity(intent);
}
});
Drawable cachedImage2 = asyncImageLoader.loadDrawable(picurl,pic, new ImageCallback(){
@Override
public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl) {
showImg(imageView,imageDrawable);
}
});
if (cachedImage2 == null)
{
//pic.setImageResource(R.drawable.usericon);
}
else
{
showImg(pic,cachedImage2);
}
}
}
}catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
url = "http://api.t.sina.com.cn/statuses/counts.json";
params=new ArrayList();
params.add(new BasicNameValuePair("source", auth.consumerKey));
params.add(new BasicNameValuePair("ids", id));
response =auth.SignRequest(user.getToken(), user.getTokenSecret(), url, params);
if (200 == response.getStatusLine().getStatusCode()){
try {
InputStream is = response.getEntity().getContent();
Reader reader = new BufferedReader(new InputStreamReader(is), 4000);
StringBuilder buffer = new StringBuilder((int) response.getEntity().getContentLength());
try {
char[] tmp = new char[1024];
int l;
while ((l = reader.read(tmp)) != -1) {
buffer.append(tmp, 0, l);
}
} finally {
reader.close();
}
String string = buffer.toString();
response.getEntity().consumeContent();
JSONArray data=new JSONArray(string);
if(data!=null){
if(data.length()>0){
JSONObject d=data.getJSONObject(0);
String comments=d.getString("comments");
String rt=d.getString("rt");
Button btn_gz=(Button)findViewById(R.id.btn_gz);
btn_gz.setText(" 轉發("+rt+")");
Button btn_pl=(Button)findViewById(R.id.btn_pl);
btn_pl.setText(" 評論("+comments+")");
}
}
}
catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
}
private void view(String id){
user=ConfigHelper.nowUser;
OAuth auth=new OAuth();
String url = "http://api.t.sina.com.cn/statuses/show/:id.json";
List params=new ArrayList();
params.add(new BasicNameValuePair("source", auth.consumerKey));
params.add(new BasicNameValuePair("id", id));
HttpResponse response =auth.SignRequest(user.getToken(), user.getTokenSecret(), url, params);
if (200 == response.getStatusLine().getStatusCode()){
try {
InputStream is = response.getEntity().getContent();
Reader reader = new BufferedReader(new InputStreamReader(is), 4000);
StringBuilder buffer = new StringBuilder((int) response.getEntity().getContentLength());
try {
char[] tmp = new char[1024];
int l;
while ((l = reader.read(tmp)) != -1) {
buffer.append(tmp, 0, l);
}
} finally {
reader.close();
}
String string = buffer.toString();
//Log.e("json", "rs:" + string);
response.getEntity().consumeContent();
JSONObject data=new JSONObject(string);
if(data!=null){
JSONObject u=data.getJSONObject("user");
String userName=u.getString("screen_name");
String userIcon=u.getString("profile_image_url");
Log.e("userIcon", userIcon);
String time=data.getString("created_at");
String text=data.getString("text");
TextView utv=(TextView)findViewById(R.id.user_name);
utv.setText(userName);
TextView ttv=(TextView)findViewById(R.id.text);
ttv.setText(text);
ImageView iv=(ImageView)findViewById(R.id.user_icon);
AsyncImageLoader asyncImageLoader = new AsyncImageLoader();
Drawable cachedImage = asyncImageLoader.loadDrawable(userIcon,iv, new ImageCallback(){
@Override
public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl) {
imageView.setImageDrawable(imageDrawable);
}
});
if (cachedImage == null)
{
iv.setImageResource(R.drawable.usericon);
}
else
{
iv.setImageDrawable(cachedImage);
}
if(data.has("bmiddle_pic")){
String picurl=data.getString("bmiddle_pic");
String picurl2=data.getString("original_pic");
ImageView pic=(ImageView)findViewById(R.id.pic);
pic.setTag(picurl2);
pic.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Object obj=v.getTag();
Intent intent = new Intent(ViewActivity.this,ImageActivity.class);
Bundle b=new Bundle();
b.putString("url", obj.toString());
intent.putExtras(b);
startActivity(intent);
}
});
Drawable cachedImage2 = asyncImageLoader.loadDrawable(picurl,pic, new ImageCallback(){
@Override
public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl) {
showImg(imageView,imageDrawable);
}
});
if (cachedImage2 == null)
{
//pic.setImageResource(R.drawable.usericon);
}
else
{
showImg(pic,cachedImage2);
}
}
}
}catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
url = "http://api.t.sina.com.cn/statuses/counts.json";
params=new ArrayList();
params.add(new BasicNameValuePair("source", auth.consumerKey));
params.add(new BasicNameValuePair("ids", id));
response =auth.SignRequest(user.getToken(), user.getTokenSecret(), url, params);
if (200 == response.getStatusLine().getStatusCode()){
try {
InputStream is = response.getEntity().getContent();
Reader reader = new BufferedReader(new InputStreamReader(is), 4000);
StringBuilder buffer = new StringBuilder((int) response.getEntity().getContentLength());
try {
char[] tmp = new char[1024];
int l;
while ((l = reader.read(tmp)) != -1) {
buffer.append(tmp, 0, l);
}
} finally {
reader.close();
}
String string = buffer.toString();
response.getEntity().consumeContent();
JSONArray data=new JSONArray(string);
if(data!=null){
if(data.length()>0){
JSONObject d=data.getJSONObject(0);
String comments=d.getString("comments");
String rt=d.getString("rt");
Button btn_gz=(Button)findViewById(R.id.btn_gz);
btn_gz.setText(" 轉發("+rt+")");
Button btn_pl=(Button)findViewById(R.id.btn_pl);
btn_pl.setText(" 評論("+comments+")");
}
}
}
catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
}
在上面的方法中對于微網誌中包含的圖檔顯示尺寸進行了特别的處理,如果直接把擷取的圖檔顯示在ImageView中,因為當圖檔寬高超過手機螢幕的時候,系統會自動按照手機的螢幕按比例縮放圖檔進行顯示,但是我發現一個現象圖檔的高雖然是按照比例縮小了,但是圖檔占據的高仍舊是原來圖檔的高度照成真實圖檔和文字内容之間多了很高的一塊空白,這個現象非常的奇怪,是以我寫了如下方法進行處理:
view plaincopy to clipboardprint?
private void showImg(ImageView view,Drawable img){
int w=img.getIntrinsicWidth();
int h=img.getIntrinsicHeight();
Log.e("w", w+"/"+h);
if(w>300)
{
int hh=300*h/w;
Log.e("hh", hh+"");
LayoutParams para=view.getLayoutParams();
para.width=300;
para.height=hh;
view.setLayoutParams(para);
}
view.setImageDrawable(img);
}
private void showImg(ImageView view,Drawable img){
int w=img.getIntrinsicWidth();
int h=img.getIntrinsicHeight();
Log.e("w", w+"/"+h);
if(w>300)
{
int hh=300*h/w;
Log.e("hh", hh+"");
LayoutParams para=view.getLayoutParams();
para.width=300;
para.height=hh;
view.setLayoutParams(para);
}
view.setImageDrawable(img);
}
本篇到這裡就結束了,請繼續關注下一篇。
關于微網誌服務端API的OAuth認證實作
6 天前 上傳
下載下傳附件 (18.96 KB)
新浪微網誌跟update相關的api已經挂了很多天了一直沒有恢複正常,傳回錯誤:40070 Error limited application access api!,新浪開放平台的論壇裡n多的人都在等這個恢複,新浪官方也相當的惡心出問題了連個公告都沒有,既不說什麼原因又不說什麼時候能恢複。還是有版主說是api正在更新禮拜1恢複正常今天都禮拜2了還是不行。基于這個原因我的android版的新浪微部落格戶端已經停工好幾天了,剛好是跟update相關的一些功能。 用戶端開發不成了,就自己做做服務端程式,提供類似新浪微網誌rest api服務, api其實說簡單也很簡單了,無法是通過連結對外提供json或者xml格式的資料和接收外部提供的資料進去相應的存儲、删除、更新等操作。過程中碰到的最麻煩的問題就是OAuth認證功能了,在做android版的新浪微部落格戶端時候也花了蠻長的時間對OAuth認證進行研究,在用戶端原先是采用了oauth-signpost開源項目,後來由于某些原因就放棄了這個開源類庫,自己重新寫了OAuth認證部分的實作, 現在做服務端的OAuth認證,其實有過做用戶端的經驗做服務端也差不多,簡單的說無非是用戶端對參數字元串進行簽名然後把簽名值傳輸到服務端,服務端也對同樣對參數字元串進行簽名,把從用戶端傳過來的簽名值進去比較,簡單的說就這麼個過程,具體實作肯定比這個要複雜多了,不明真相的同學可以google一下OAuth進行深入的學習研究了。 服務端程式用asp.net和C#編寫了而非java,理由很簡單本人對.net更加熟悉。由于想快速的實作效果采用了oauth-dot-net開源項目并沒有全部自己寫。
一、首先建立名為Rest Api的ASP.NET Web應用程式,然後添加 oauth-dot-net開源項目相關的幾個dll(Castle.Core.dll、Castle.MicroKernel.dll、Castle.Windsor.dll、CommonServiceLocator.WindsorAdapter.dll、Microsoft.Practices.ServiceLocation.dll、OAuth.Net.Common.dll、OAuth.Net.Components.dll、OAuth.Net.ServiceProvider.dll)。
二、在Web.config檔案裡添加相應的配置,具體可以參考OAuth.Net.Examples.EchoServiceProvider項目,然後在Global.asax.cs添加如下代碼:
view plaincopy to clipboardprint?
public override void Init()
{
IServiceLocator injector =
new WindsorServiceLocator(
new WindsorContainer(
new XmlInterpreter(
new ConfigResource("oauth.net.components"))));
ServiceLocator.SetLocatorProvider(() => injector);
}
public override void Init()
{
IServiceLocator injector =
new WindsorServiceLocator(
new WindsorContainer(
new XmlInterpreter(
new ConfigResource("oauth.net.components"))));
ServiceLocator.SetLocatorProvider(() => injector);
}
接下來是比較重要,就是request_token、authorize、access_token的實作,OAuth認證實作的幾個過程,不了解可以看android開發我的新浪微部落格戶端-OAuth篇(2.1) ,具體代碼實作很多是參考OAuth.Net.Examples.EchoServiceProvider示例項目。
三、 首先建立ConsumerStore.cs類,用來存儲Consumer資訊,由于測試項目是以存儲在記憶體中并沒有考慮儲存到資料庫,真實項目的時候請把相應的Consumer資訊儲存到資料庫中。Consumer資訊對應新浪微網誌其實就是應用的App Key和App Secret,當開發者在新浪微網誌建一個新的應用擷取App Key和App Secret,是以完整的應該還需要一個開發一個提供給第三方開發者申請擷取App Key和App Secret的功能頁面,這裡就不具體實作,直接在代碼裡寫死了一個名為測試應用的Consumer,App Key:2433927322,App Secret:87f042c9e8183cbde0f005a00db1529f,這個提供給用戶端測試用。 具體代碼如下:
view plaincopy to clipboardprint?
public sealed class ConsumerStore : InMemoryConsumerStore, IConsumerStore
{
internal static readonly IConsumer FixedConsumer = new OAuthConsumer("2433927322", "87f042c9e8183cbde0f005a00db1529f", "測試應用", ConsumerStatus.Valid);
public ConsumerStore()
{
this.ConsumerDictionary.Add(
ConsumerStore.FixedConsumer.Key,
ConsumerStore.FixedConsumer);
}
public override bool Add(IConsumer consumer)
{
throw new NotSupportedException("Consumers cannot be added to this store--it is fixed.");
}
public override bool Contains(string consumerKey)
{
return ConsumerStore.FixedConsumer.Key.Equals(consumerKey);
}
public override bool Update(IConsumer consumer)
{
throw new NotSupportedException("Consumers cannot be updated in this store--it is fixed.");
}
public override bool Remove(IConsumer consumer)
{
throw new NotSupportedException("Consumers cannot be removed from this store--it is fixed.");
}
}
public sealed class ConsumerStore : InMemoryConsumerStore, IConsumerStore
{
internal static readonly IConsumer FixedConsumer = new OAuthConsumer("2433927322", "87f042c9e8183cbde0f005a00db1529f", "測試應用", ConsumerStatus.Valid);
public ConsumerStore()
{
this.ConsumerDictionary.Add(
ConsumerStore.FixedConsumer.Key,
ConsumerStore.FixedConsumer);
}
public override bool Add(IConsumer consumer)
{
throw new NotSupportedException("Consumers cannot be added to this store--it is fixed.");
}
public override bool Contains(string consumerKey)
{
return ConsumerStore.FixedConsumer.Key.Equals(consumerKey);
}
public override bool Update(IConsumer consumer)
{
throw new NotSupportedException("Consumers cannot be updated in this store--it is fixed.");
}
public override bool Remove(IConsumer consumer)
{
throw new NotSupportedException("Consumers cannot be removed from this store--it is fixed.");
}
}
四、接下來就是request_token功能,建立RequestTokenHandler.cs ,這個是OAuth.Net.ServiceProvider.RequestTokenHandler子類,并且是httpHandlers是以需要在Web.config中添加httpHandlers配置,這個用來接收用戶端程式的請求,傳回給用戶端程式Request Token和Request Secret用,具體代碼如下:
view plaincopy to clipboardprint?
public sealed class RequestTokenHandler : OAuth.Net.ServiceProvider.RequestTokenHandler
{
protected override void IssueRequestToken(HttpContext httpContext, OAuthRequestContext requestContext)
{
//産生RequestToken
IRequestToken token = this.GenerateRequestToken(httpContext, requestContext);
requestContext.RequestToken = token;
Uri callbackUri;
if (Uri.TryCreate(requestContext.Parameters.Callback, UriKind.Absolute, out callbackUri))
{
if (!ServiceProviderContext.CallbackStore.ContainsCallback(token))
{
//儲存Callback位址了
ServiceProviderContext.CallbackStore.AddCallback(token, callbackUri);
}
}
else
OAuthRequestException.ThrowParametersRejected(new string[] { Constants.CallbackParameter }, "Not a valid Uri.");
//把token.Token和token.Secret輸出到用戶端,
requestContext.ResponseParameters[Constants.TokenParameter] = token.Token;
requestContext.ResponseParameters[Constants.TokenSecretParameter] = token.Secret;
}
protected override IRequestToken GenerateRequestToken(HttpContext httpContext, OAuthRequestContext requestContext)
{
return ServiceProviderContext.TokenGenerator.CreateRequestToken(requestContext.Consumer, requestContext.Parameters);
}
}
public sealed class RequestTokenHandler : OAuth.Net.ServiceProvider.RequestTokenHandler
{
protected override void IssueRequestToken(HttpContext httpContext, OAuthRequestContext requestContext)
{
//産生RequestToken
IRequestToken token = this.GenerateRequestToken(httpContext, requestContext);
requestContext.RequestToken = token;
Uri callbackUri;
if (Uri.TryCreate(requestContext.Parameters.Callback, UriKind.Absolute, out callbackUri))
{
if (!ServiceProviderContext.CallbackStore.ContainsCallback(token))
{
//儲存Callback位址了
ServiceProviderContext.CallbackStore.AddCallback(token, callbackUri);
}
}
else
OAuthRequestException.ThrowParametersRejected(new string[] { Constants.CallbackParameter }, "Not a valid Uri.");
//把token.Token和token.Secret輸出到用戶端,
requestContext.ResponseParameters[Constants.TokenParameter] = token.Token;
requestContext.ResponseParameters[Constants.TokenSecretParameter] = token.Secret;
}
protected override IRequestToken GenerateRequestToken(HttpContext httpContext, OAuthRequestContext requestContext)
{
return ServiceProviderContext.TokenGenerator.CreateRequestToken(requestContext.Consumer, requestContext.Parameters);
}
}
五、 接着是authorize功能,建立名為authorize.aspx的頁面,用來給使用者輸入賬号和密碼進行授權的頁面,這個頁面很簡單具體如下圖,在這個頁面中擷取使用者輸入的賬戶和密碼跟資料庫中存儲的使用者賬号和密碼進行驗證,如果驗證通過傳回之前用戶端提供的callback位址,并且給這個位址添加一個校驗碼,具體代碼如下:
view plaincopy to clipboardprint?
public partial class authorize : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
if (loginName.Text == "test" && password.Text == "123")
{
string toke = Request.Params["oauth_token"];
IRequestToken tk = ServiceProviderContext.TokenStore.GetRequestToken(toke);
Uri callback = ServiceProviderContext.CallbackStore.GetCalback(tk);
string oauth_verifier = ServiceProviderContext.VerificationProvider.Generate(tk);
Response.Redirect(callback.ToString() + "?oauth_verifier=" + oauth_verifier);
}
}
}
public partial class authorize : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
if (loginName.Text == "test" && password.Text == "123")
{
string toke = Request.Params["oauth_token"];
IRequestToken tk = ServiceProviderContext.TokenStore.GetRequestToken(toke);
Uri callback = ServiceProviderContext.CallbackStore.GetCalback(tk);
string oauth_verifier = ServiceProviderContext.VerificationProvider.Generate(tk);
Response.Redirect(callback.ToString() + "?oauth_verifier=" + oauth_verifier);
}
}
}
六、接下來就是access_token功能,建立AccessTokenHandler.cs , 這個是OAuth.Net.ServiceProvider.AccessTokenHandler子類,并且是httpHandlers是以需要在Web.config中添加httpHandlers配置,這個用來接收用戶端程式的請求,傳回給用戶端程式Access Token和Access Secret用,具體代碼如下:
view plaincopy to clipboardprint?
public sealed class AccessTokenHandler : OAuth.Net.ServiceProvider.AccessTokenHandler
{
protected override void IssueAccessToken(HttpContext httpContext, OAuthRequestContext requestContext)
{
//産生access token
IAccessToken accessToken = this.GenerateAccessToken(httpContext, requestContext);
accessToken.Status = TokenStatus.Authorized;
// 把accessToken和accessSecret輸出到用戶端,
requestContext.ResponseParameters[Constants.TokenParameter] = accessToken.Token;
requestContext.ResponseParameters[Constants.TokenSecretParameter] = accessToken.Secret;
}
protected override IAccessToken GenerateAccessToken(HttpContext httpContext, OAuthRequestContext requestContext)
{
return ServiceProviderContext.TokenGenerator.CreateAccessToken(requestContext.Consumer, requestContext.RequestToken);
}
}
public class TokenGenerator : ITokenGenerator
{
internal static readonly IRequestToken FixedRequestToken = new OAuthRequestToken("requestkey",
"requestsecret",
ConsumerStore.FixedConsumer,
TokenStatus.Authorized,
null,
ServiceProviderContext.DummyIdentity,
new string[] { });
internal static readonly IAccessToken FixedAccessToken = new OAuthAccessToken(
"accesskey",
"accesssecret",
ConsumerStore.FixedConsumer,
TokenStatus.Authorized,
TokenGenerator.FixedRequestToken);
public IRequestToken CreateRequestToken(IConsumer consumer, OAuthParameters parameters)
{
return TokenGenerator.FixedRequestToken;
}
public IAccessToken CreateAccessToken(IConsumer consumer, IRequestToken requestToken)
{
return TokenGenerator.FixedAccessToken;
}
}
public class TokenStore : InMemoryTokenStore, ITokenStore
{
public TokenStore()
{
this.RequestTokenDictionary.Add(
TokenGenerator.FixedRequestToken.Token,
TokenGenerator.FixedRequestToken);
this.AccessTokenDictionary.Add(
TokenGenerator.FixedAccessToken.Token,
TokenGenerator.FixedAccessToken);
}
public override bool Add(IRequestToken token)
{
throw new NotSupportedException("Tokens cannot be added to the token store--it is fixed.");
}
public override bool Add(IAccessToken token)
{
throw new NotSupportedException("Tokens cannot be added to the token store--it is fixed.");
}
public override bool Update(IRequestToken token)
{
throw new NotSupportedException("Tokens cannot be updated in the token store--it is fixed.");
}
public override bool Update(IAccessToken token)
{
throw new NotSupportedException("Tokens cannot be updated in the token store--it is fixed.");
}
public override bool Remove(IRequestToken token)
{
throw new NotSupportedException("Tokens cannot be removed from the token store--it is fixed.");
}
public override bool Remove(IAccessToken token)
{
throw new NotSupportedException("Tokens cannot be removed from the token store--it is fixed.");
}
}
public sealed class AccessTokenHandler : OAuth.Net.ServiceProvider.AccessTokenHandler
{
protected override void IssueAccessToken(HttpContext httpContext, OAuthRequestContext requestContext)
{
//産生access token
IAccessToken accessToken = this.GenerateAccessToken(httpContext, requestContext);
accessToken.Status = TokenStatus.Authorized;
// 把accessToken和accessSecret輸出到用戶端,
requestContext.ResponseParameters[Constants.TokenParameter] = accessToken.Token;
requestContext.ResponseParameters[Constants.TokenSecretParameter] = accessToken.Secret;
}
protected override IAccessToken GenerateAccessToken(HttpContext httpContext, OAuthRequestContext requestContext)
{
return ServiceProviderContext.TokenGenerator.CreateAccessToken(requestContext.Consumer, requestContext.RequestToken);
}
}
public class TokenGenerator : ITokenGenerator
{
internal static readonly IRequestToken FixedRequestToken = new OAuthRequestToken("requestkey",
"requestsecret",
ConsumerStore.FixedConsumer,
TokenStatus.Authorized,
null,
ServiceProviderContext.DummyIdentity,
new string[] { });
internal static readonly IAccessToken FixedAccessToken = new OAuthAccessToken(
"accesskey",
"accesssecret",
ConsumerStore.FixedConsumer,
TokenStatus.Authorized,
TokenGenerator.FixedRequestToken);
public IRequestToken CreateRequestToken(IConsumer consumer, OAuthParameters parameters)
{
return TokenGenerator.FixedRequestToken;
}
public IAccessToken CreateAccessToken(IConsumer consumer, IRequestToken requestToken)
{
return TokenGenerator.FixedAccessToken;
}
}
public class TokenStore : InMemoryTokenStore, ITokenStore
{
public TokenStore()
{
this.RequestTokenDictionary.Add(
TokenGenerator.FixedRequestToken.Token,
TokenGenerator.FixedRequestToken);
this.AccessTokenDictionary.Add(
TokenGenerator.FixedAccessToken.Token,
TokenGenerator.FixedAccessToken);
}
public override bool Add(IRequestToken token)
{
throw new NotSupportedException("Tokens cannot be added to the token store--it is fixed.");
}
public override bool Add(IAccessToken token)
{
throw new NotSupportedException("Tokens cannot be added to the token store--it is fixed.");
}
public override bool Update(IRequestToken token)
{
throw new NotSupportedException("Tokens cannot be updated in the token store--it is fixed.");
}
public override bool Update(IAccessToken token)
{
throw new NotSupportedException("Tokens cannot be updated in the token store--it is fixed.");
}
public override bool Remove(IRequestToken token)
{
throw new NotSupportedException("Tokens cannot be removed from the token store--it is fixed.");
}
public override bool Remove(IAccessToken token)
{
throw new NotSupportedException("Tokens cannot be removed from the token store--it is fixed.");
}
}
這樣就完成了一個最最簡單小型的服務端OAuth認證,然後用android用戶端進行測試ok通過。 注意點: 一、android模拟器通路本地服務位址為10.0.2.2,比如http://localhost:3423/authorize.aspx在模拟器中用http://10.0.2.2:3423/authorize.aspx。 二、OAuth.Net類庫的OAuth.Net.Common項目中的interface ICallbackStore 添加了一個Uri GetCalback(IRequestToken token);并且在具體的實作類InMemoryCallbackStore添加了實習代碼: public Uri GetCalback(IRequestToken token) {
lock (this.callbackStore)
{
if (this.callbackStore.ContainsKey(token))
{
return this.callbackStore[token];
}
else
{
return null;
}
}
}
三、為了能用我前面做的給新浪用的android用戶端,對于類庫源代碼AccessTokenHandler的ParseParameters方法做了如下修改,因為新浪請求api的時候都會加一個source的參數: protected virtual void ParseParameters(HttpContext httpContext, OAuthRequestContext requestContext) {
.......
parameters.AllowOnly(
Constants.ConsumerKeyParameter,
Constants.TokenParameter,
Constants.SignatureMethodParameter,
Constants.SignatureParameter,
Constants.TimestampParameter,
Constants.NonceParameter,
Constants.VerifierParameter,
Constants.VersionParameter, // (optional)
Constants.RealmParameter, // (optional)
"source");
......
}
android開發我的新浪微部落格戶端-大圖浏覽以及儲存篇(7)
6 天前 上傳
下載下傳附件 (129.72 KB)
6 天前 上傳
下載下傳附件 (73.8 KB) 在閱讀微網誌的功能篇中,如果微網誌包含了圖檔就會在微網誌正文下面顯示該張圖檔,但是這個圖檔隻是張縮略圖,這樣就需要提供一個能放大縮小檢視這張圖檔的功能,當點選正文中的縮略圖的時候顯示一個簡單的圖檔浏覽器功能,提供圖檔的放大、縮小、拖拽操作友善使用者檢視圖檔,同時也提供儲存圖檔到手機的功能。本功能的UI比較簡單就不單獨分篇講了,具體的實作效果如上圖。
建立ImageActivity.java作為圖檔浏覽Activity,在res/layout下建立image.xml的Layout作為圖檔浏覽的布局檔案,image.xml布局代碼很簡單了就不詳細解釋了直接貼代碼:
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="41px"
android:background="@drawable/imagebar_bg">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="2">
<Button
android:id="@+id/returnBtn"
android:layout_width="63px"
android:layout_height="29px"
android:layout_centerInParent="true"
android:text="傳回"
android:textColor="#ffffff"
android:background="@drawable/btn1_bg">
</Button>
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="浏覽圖檔"
android:textColor="#ffffff">
</TextView>
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="2">
<Button
android:id="@+id/downBtn"
android:layout_width="60px"
android:layout_height="29px"
android:layout_centerInParent="true"
android:text="下載下傳"
android:textColor="#ffffff"
android:background="@drawable/btn2_bg">
</Button>
</RelativeLayout>
</LinearLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<MySinaWeiBo.ui.ImageZoomView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pic"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</MySinaWeiBo.ui.ImageZoomView>
<ZoomControls
android:id="@+id/zoomCtrl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true">
</ZoomControls>
</RelativeLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="41px"
android:background="@drawable/imagebar_bg">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="2">
<Button
android:id="@+id/returnBtn"
android:layout_width="63px"
android:layout_height="29px"
android:layout_centerInParent="true"
android:text="傳回"
android:textColor="#ffffff"
android:background="@drawable/btn1_bg">
</Button>
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="浏覽圖檔"
android:textColor="#ffffff">
</TextView>
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="2">
<Button
android:id="@+id/downBtn"
android:layout_width="60px"
android:layout_height="29px"
android:layout_centerInParent="true"
android:text="下載下傳"
android:textColor="#ffffff"
android:background="@drawable/btn2_bg">
</Button>
</RelativeLayout>
</LinearLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<MySinaWeiBo.ui.ImageZoomView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pic"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</MySinaWeiBo.ui.ImageZoomView>
<ZoomControls
android:id="@+id/zoomCtrl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true">
</ZoomControls>
</RelativeLayout>
</LinearLayout>
上面的代碼中用到了一個自定義控件MySinaWeiBo.ui.ImageZoomView,這個就是整個功能的核心部分,用來實作圖檔的放大、縮小、拖拽的一個圖檔顯示控件,這個控件非我原創,是參考了Android one finger zoom tutorial 這篇部落格寫出來的,是以我在這裡也不貼實作代碼了,有興趣的大家可以直接看看這個文章。 接下要做的就是用這個ImageZoomView來顯示圖檔,在閱讀微網誌内容的頁面中當點選内容中的縮略圖檔的時候會把這個縮略圖對應的原圖的url傳給目前的這個ImageActivity,那麼在ImageActivity的onCreate方法中根據這個url擷取圖檔并且設定給ImageZoomView。在onCreate方法中代碼如下:
view plaincopy to clipboardprint?
@Override
public void onCreate(Bundle savedInstanceState) {
。。。。。
Intent i=this.getIntent();
if(i!=null){
Bundle b=i.getExtras();
if(b!=null){
if(b.containsKey("url")){
String url = b.getString("url");
mZoomView=(ImageZoomView)findViewById(R.id.pic);
Drawable img= AsyncImageLoader.loadImageFromUrl(url);
image=drawableToBitmap(img);
mZoomView.setImage(image);
mZoomState = new ZoomState();
mZoomView.setZoomState(mZoomState);
mZoomListener = new SimpleZoomListener();
mZoomListener.setZoomState(mZoomState);
mZoomView.setOnTouchListener(mZoomListener);
resetZoomState();
}
}
}
。。。。。。
}
@Override
public void onCreate(Bundle savedInstanceState) {
。。。。。
Intent i=this.getIntent();
if(i!=null){
Bundle b=i.getExtras();
if(b!=null){
if(b.containsKey("url")){
String url = b.getString("url");
mZoomView=(ImageZoomView)findViewById(R.id.pic);
Drawable img= AsyncImageLoader.loadImageFromUrl(url);
image=drawableToBitmap(img);
mZoomView.setImage(image);
mZoomState = new ZoomState();
mZoomView.setZoomState(mZoomState);
mZoomListener = new SimpleZoomListener();
mZoomListener.setZoomState(mZoomState);
mZoomView.setOnTouchListener(mZoomListener);
resetZoomState();
}
}
}
。。。。。。
}
view plaincopy to clipboardprint?
private void resetZoomState() {
mZoomState.setPanX(0.5f);
mZoomState.setPanY(0.5f);
final int mWidth = image.getWidth();
final int vWidth= mZoomView.getWidth();
Log.e("iw:",vWidth+"");
mZoomState.setZoom(1f);
mZoomState.notifyObservers();
}
public Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
return bitmap;
}
private void resetZoomState() {
mZoomState.setPanX(0.5f);
mZoomState.setPanY(0.5f);
final int mWidth = image.getWidth();
final int vWidth= mZoomView.getWidth();
Log.e("iw:",vWidth+"");
mZoomState.setZoom(1f);
mZoomState.notifyObservers();
}
public Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
return bitmap;
}
view plaincopy to clipboardprint?
ZoomControls zoomCtrl = (ZoomControls) findViewById(R.id.zoomCtrl);
zoomCtrl.setOnZoomInClickListener(new OnClickListener(){
@Override
public void onClick(View view) {
float z= mZoomState.getZoom()+0.25f;
mZoomState.setZoom(z);
mZoomState.notifyObservers();
}
});
zoomCtrl.setOnZoomOutClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
float z= mZoomState.getZoom()-0.25f;
mZoomState.setZoom(z);
mZoomState.notifyObservers();
}
});
ZoomControls zoomCtrl = (ZoomControls) findViewById(R.id.zoomCtrl);
zoomCtrl.setOnZoomInClickListener(new OnClickListener(){
@Override
public void onClick(View view) {
float z= mZoomState.getZoom()+0.25f;
mZoomState.setZoom(z);
mZoomState.notifyObservers();
}
});
zoomCtrl.setOnZoomOutClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
float z= mZoomState.getZoom()-0.25f;
mZoomState.setZoom(z);
mZoomState.notifyObservers();
}
});
這樣一個簡單的圖檔浏覽器功能就完成了,支援放大縮小并且還能拖拽,基本上達到應用需求。
本文來自CSDN部落格,轉載請标明出處:http://blog.csdn.net/mgf860704/archive/2011/04/06/6304089.aspx