android開發新浪微博客戶端 完整攻略 [新手必讀][轉]2011-04-06 10:49:19
http://www.eoeandroid.com/forum-viewthread-tid-67298-fromuid-106432.html
開始接觸學習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先做了一下設計效果圖如下:
一、接下來的任務就是在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的父控件用來控制居中對齊已經底部對齊的實現,具體代碼如下:代碼
- <?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部分代碼如下:
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- }
然後運行項目可以在模擬器中顯示,上面的幾個圖片都按照設計的位置和效果進行顯示只是整個頁面的背景還是黑色的,接下來就是背景部分的顯示實現,由於爲了實現橫豎屏切換顯示,背景圖的顯示採用代碼進行控制顯示,首先用如下方法獲取當前手機是橫屏還是豎屏:
- //獲取屏幕方向
- 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的公共方法用來實現屏幕背景的自動切換,後面的幾乎每一個功能頁面都需要用到這個方法
- 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方法進行屏幕自動切換:
- LinearLayout layout=(LinearLayout)findViewById(R.id.layout);
- //背景自動適應
- AndroidHelper.AutoBackground(this, layout, R.drawable.bg_v, R.drawable.bg_h);
到此完成了載入頁面的UI部分的實現,測試運行模擬器中查看效果,基本上跟最上面的設計效果圖相符,測試效果圖如下:
通過上一篇文章(android開發我的新浪微博客戶端-載入頁面UI篇(1.1))已經完成了載入頁面的UI部分的實現,效果如上圖,接下來在上面的基礎上完成載入頁面的功能代碼。
首先說明一下新浪微博提供了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數據表的創建、更新等,代碼如下:
- 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類文件實現用戶記錄的創建、更新、刪除等,代碼如下
- 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方法添加代碼:
- 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的認證方式。
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開源項目簡單了很多。具體代碼如下:
- 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的效果圖了直接貼實現後的功能截圖如下:
看上面的圖,其實這個頁面的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:
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.authorize);
- .......
- }
接下來是本文的重點部分,半透明彈窗用Dialog控件進行實現,首先爲這個半透明彈窗新建一個名爲dialog.xml的Layout,這個Layout主要是對4個元素進行佈局,如圖所示分別爲i小圖標、信息提示、中間文字、開始按鈕,首先用LinearLayout對i小圖標和信息提示進行水平佈局,中間文字以一個TextView跟在下面,對於開始按鈕是用RelativeLayout進行底部對齊顯示。具體代碼如下:
- <?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樣式文件,具體代碼如下:
- <?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 diaView=View.inflate(this, R.layout.dialog, null);
- dialog=new Dialog(AuthorizeActivity.this,R.style.dialog);
- dialog.setContentView(diaView);
- dialog.show();
- ......
最後運行查看效果,到這裏我們的任務已經完成了。請關注下一篇功能篇。
android開發我的新浪微博客戶端-用戶授權頁面功能篇(3.2)
在上一篇實現了用戶授權頁面的UI,如上圖,接下來要做的就是在這個基礎上完成功能部分真正實現用戶的授權認證,這一篇是android開發我的新浪微博客戶端-OAuth篇(2.1)的具體應用篇原理就不多解釋了不懂的看OAuth篇即可。認證過程從點擊開始按鈕然後跳轉到新浪的授權頁面,接着用戶在新浪的頁面裏輸入自己的賬戶和密碼確定後返回用戶授權頁面。首先給開始按鈕添加點擊事件代碼,代碼中主要是調用我們前面android開發我的新浪微博客戶端-OAuth篇(2.1)完成的OAuth類的RequestAccessToken方法用來獲取oauth_verifier,具體代碼如下:
- 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參數。
- <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添加如下方法:
- @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添加如下代碼:
- 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)
首先回顧一下功能流程當用戶開啓軟件顯示載入頁面時程序首先去sqlite庫查詢是否已經保存有用戶的新浪微博的UserID號、Access Token、Access Secret的記錄如果沒有一條記錄那麼跳轉到用戶授權功能頁面,這個已經由上面兩篇文章實現了,如果有記錄那麼頁面跳轉到用戶登錄頁面,也就是本篇以及下篇要實現的功能,本篇講UI的實現,本項目支持多微博賬號了,也就是用戶可以設置多個微博賬號,登錄的時候選擇其中的一個登錄,具體效果如上圖,新建名LoginActivity.java的Activity並且在AndroidManifest.xml中進行相應配置,這個頁面就是我們要實現的用戶登錄頁面。
看上面的效果,首先頁面分3部分實現,背景部分、底部菜單部分、用戶選擇以及頭像顯示部分,首先在res/layout的目錄下新建名爲login.xml的layout,然後根據頁面顯示要求編寫如下的佈局控制:
- <?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的樣式,具體解釋可以參考前面的戶授權驗證頁面功能,內容如下:
- <?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文件,內容如下:
- <?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方法中添加如下代碼:
- 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控件來顯示賬號列表,首先是從數據庫中獲取所有的賬戶記錄然後設置默認選中的用戶賬號代碼如下:
- 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即可,代碼如下:
- 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中代碼如下:
- 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,使得點擊後以當前選擇賬號進入微博首頁,代碼如下:
- @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中的賬戶名稱,代碼如下:
- @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)
在前篇完成了用戶登錄功能後開始用戶首頁的開發,用戶的首頁主要的內容是當前登錄用戶關注的微博列表,本篇先來講講UI的實現,效果如上圖,整個頁面分爲上、中、下三部分,上面部分是工具條,顯示當前登錄用戶的暱稱以及寫微博、刷新兩個功能按鈕;中間部分是當前用戶關注的最新微博列表,下面部分是功能切換欄,用來進行各個功能之間的切換。
首先新建名爲HomeActivity.java的Activity作爲用戶首頁,然後在res/layout目錄下新建名爲home.xml的Layout,具體代碼如下:
- <?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,內容如下:
- <?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,
八張不同的小圖片分別代表每旋轉45度圖片,八張剛好是360度。把這些圖片添加到res/drawable-mdpi目錄中。然後在res/anim目錄下新建名爲loading.xml動畫文件,內容如下:
- <?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)
上一篇完成用戶首頁的UI實現,本篇接下來講功能部分的實現,本頁面主要的功能就用戶關注的最新微博列表,從上一篇中知道本列表是用ID爲Msglist的ListView控件來實現,本篇的主要就講解如果獲取微博列表數據給這個ListView提供顯示數據。ListView每一條子數據分別由用戶頭像、用戶暱稱、發佈時間、是否包含照片、微博內容這五部分組成,根據這五部分定義一個名爲WeiBoInfo.java實體類,代碼如下:
- 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子項的顯示部件,代碼很簡單不多解釋了,直接看下面代碼:
- <?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,代碼如下:
- 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的代碼如下:
- 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();代碼如下:
- @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)
上一篇完成了微博列表的功能,本篇接着做預讀微博的功能,本篇主要講講UI部分的實現,最終實現的效果如上圖所示。整個顯示頁面從上往下分爲四部分,第一部分頂部工具條、第二部分作者頭像和名稱、第三部分微博正文、第四部分功能按鈕區。新建名爲ViewActivity.java作爲閱讀微博的頁面,再res/layout目錄下新建名爲view.xml的Layout,代碼如下:
- <?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用來實現點擊上去切換圖片的效果,具體代碼如下:
- <?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)
注:最近由於OAuth上傳圖片碰到了難題,一直在做這方面的研究導致博客很久沒有更新。
在上面一篇中已經實現了預讀微博的UI界面,效果如上圖,接下來完成功能部分的代碼,當用戶在上一個列表界面的列表中點擊某一條微博的時候顯示這個閱讀微博的界面,在這個界面中根據傳來的微博ID,然後根據這個ID通過api獲取微博的具體內容進行顯示。
在ViewActivity.class的onCreate方法中添加如下代碼:
- 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的方法異步載入圖片並且進行顯示,同時在這個方法中還要獲取本條微博被轉發的次數以及評論的次數,具體代碼如下:
- 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中,因爲當圖片寬高超過手機屏幕的時候,系統會自動按照手機的屏幕按比例縮放圖片進行顯示,但是我發現一個現象圖片的高雖然是按照比例縮小了,但是圖片佔據的高仍舊是原來圖片的高度照成真實圖片和文字內容之間多了很高的一塊空白,這個現象非常的奇怪,所以我寫了如下方法進行處理:
- 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認證實現
新浪微博跟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添加如下代碼:
- 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,這個提供給客戶端測試用。 具體代碼如下:
- 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用,具體代碼如下:
- 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地址,並且給這個地址添加一個校驗碼,具體代碼如下:
- 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用,具體代碼如下:
- 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)
在閱讀微博的功能篇中,如果微博包含了圖片就會在微博正文下面顯示該張圖片,但是這個圖片只是張縮略圖,這樣就需要提供一個能放大縮小查看這張圖片的功能,當點擊正文中的縮略圖的時候顯示一個簡單的圖片瀏覽器功能,提供圖片的放大、縮小、拖拽操作方便用戶查看圖片,同時也提供保存圖片到手機的功能。本功能的UI比較簡單就不單獨分篇講了,具體的實現效果如上圖。
新建ImageActivity.java作爲圖片瀏覽Activity,在res/layout下新建image.xml的Layout作爲圖片瀏覽的佈局文件,image.xml佈局代碼很簡單了就不詳細解釋了直接貼代碼:
- <?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方法中代碼如下:
- @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();
- }
- }
- }
- 。。。。。。
- }
- 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;
- }
- 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();
- }
- });
這樣一個簡單的圖片瀏覽器功能就完成了,支持放大縮小並且還能拖拽,基本上達到應用需求。