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的父控件用来控制居中对齐已经底部对齐的实现,具体代码如下:代码

[java] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  3. android:id="@+id/layout"
  4. android:orientation="vertical"
  5. android:layout_width="fill_parent"
  6. android:layout_height="fill_parent">
  7. <ImageView
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:src="@drawable/ver"
  11. android:layout_marginTop="15dip"
  12. android:layout_marginLeft="15dip">
  13. </ImageView>
  14. <RelativeLayout
  15. android:layout_width="fill_parent"
  16. android:layout_height="fill_parent">
  17. <ImageView
  18. android:layout_width="wrap_content"
  19. android:layout_height="wrap_content"
  20. android:src="@drawable/logo"
  21. android:layout_centerInParent="true">
  22. </ImageView>
  23. <ImageView
  24. android:layout_width="wrap_content"
  25. android:layout_height="wrap_content"
  26. android:src="@drawable/dev"
  27. android:layout_alignParentBottom="true"
  28. android:layout_alignParentRight="true"
  29. android:layout_marginRight="5dip"
  30. android:layout_marginBottom="35dip">
  31. </ImageView>
  32. </RelativeLayout>
  33. </LinearLayout>

三、在ec打开名为MainActivity的Activity源代码文件进行编辑,onCreate部分代码如下:

[java] view plain copy
  1. publicvoidonCreate(BundlesavedInstanceState){
  2. super.onCreate(savedInstanceState);
  3. setContentView(R.layout.main);
  4. }

然后运行项目可以在模拟器中显示,上面的几个图片都按照设计的位置和效果进行显示只是整个页面的背景还是黑色的,接下来就是背景部分的显示实现,由于为了实现横竖屏切换显示,背景图的显示采用代码进行控制显示,首先用如下方法获取当前手机是横屏还是竖屏:

[java] view plain copy
  1. //获取屏幕方向
  2. publicstaticintScreenOrient(Activityactivity)
  3. {
  4. intorient=activity.getRequestedOrientation();
  5. if(orient!=ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE&&orient!=ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){
  6. //宽>高为横屏,反正为竖屏
  7. WindowManagerwindowManager=activity.getWindowManager();
  8. Displaydisplay=windowManager.getDefaultDisplay();
  9. intscreenWidth=display.getWidth();
  10. intscreenHeight=display.getHeight();
  11. orient=screenWidth<screenHeight?ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
  12. }
  13. returnorient;
  14. }

然后编写一个名为AutoBackground的公共方法用来实现屏幕背景的自动切换,后面的几乎每一个功能页面都需要用到这个方法

[java] view plain copy
  1. publicstaticvoidAutoBackground(Activityactivity,Viewview,intBackground_v,intBackground_h)
  2. {
  3. intorient=ScreenOrient(activity);
  4. if(orient==ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){//纵向
  5. view.setBackgroundResource(Background_v);
  6. }else{//横向
  7. view.setBackgroundResource(Background_h);
  8. }
  9. }

完成上述两方法后在 MainActivity的onCreate方法中调用AutoBackground方法进行屏幕自动切换:

[java] view plain copy
  1. LinearLayoutlayout=(LinearLayout)findViewById(R.id.layout);
  2. //背景自动适应
  3. 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数据表的创建、更新等,代码如下:

[java] view plain copy
  1. publicclassSqliteHelperextendsSQLiteOpenHelper{
  2. //用来保存
  3. UserID、AccessToken、AccessSecret
  4. 的表名
  5. publicstaticfinalStringTB_NAME="users";
  6. publicSqliteHelper(Contextcontext,Stringname,CursorFactoryfactory,intversion){
  7. super(context,name,factory,version);
  8. }
  9. //创建表
  10. @Override
  11. publicvoidonCreate(SQLiteDatabasedb){
  12. db.execSQL("CREATETABLEIFNOTEXISTS"+
  13. TB_NAME+"("+
  14. UserInfo.ID+"integerprimarykey,"+
  15. UserInfo.USERID+"varchar,"+
  16. UserInfo.TOKEN+"varchar,"+
  17. UserInfo.TOKENSECRET+"varchar,"+
  18. UserInfo.USERNAME+"varchar,"+
  19. UserInfo.USERICON+"blob"+
  20. ")"
  21. );
  22. Log.e("Database","onCreate");
  23. }
  24. //更新表
  25. @Override
  26. publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){
  27. db.execSQL("DROPTABLEIFEXISTS"+TB_NAME);
  28. onCreate(db);
  29. Log.e("Database","onUpgrade");
  30. }
  31. //更新列
  32. publicvoidupdateColumn(SQLiteDatabasedb,StringoldColumn,StringnewColumn,StringtypeColumn){
  33. try{
  34. db.execSQL("ALTERTABLE"+
  35. TB_NAME+"CHANGE"+
  36. oldColumn+""+newColumn+
  37. ""+typeColumn
  38. );
  39. }catch(Exceptione){
  40. e.printStackTrace();
  41. }
  42. }
  43. }

接下来新建名为DataHelper.java类文件实现用户记录的创建、更新、删除等,代码如下

[java] view plain copy
  1. publicclassDataHelper{
  2. //数据库名称
  3. privatestaticStringDB_NAME="mysinaweibo.db";
  4. //数据库版本
  5. privatestaticintDB_VERSION=2;
  6. privateSQLiteDatabasedb;
  7. privateSqliteHelperdbHelper;
  8. publicDataHelper(Contextcontext){
  9. dbHelper=newSqliteHelper(context,DB_NAME,null,DB_VERSION);
  10. db=dbHelper.getWritableDatabase();
  11. }
  12. publicvoidClose()
  13. {
  14. db.close();
  15. dbHelper.close();
  16. }
  17. //获取users表中的UserID、AccessToken、AccessSecret的记录
  18. publicList<UserInfo>GetUserList(BooleanisSimple)
  19. {
  20. List<UserInfo>userList=newArrayList<UserInfo>();
  21. Cursorcursor=db.query(SqliteHelper.TB_NAME,null,null,null,null,null,UserInfo.ID+"DESC");
  22. cursor.moveToFirst();
  23. while(!cursor.isAfterLast()&&(cursor.getString(1)!=null)){
  24. UserInfouser=newUserInfo();
  25. user.setId(cursor.getString(0));
  26. user.setUserId(cursor.getString(1));
  27. user.setToken(cursor.getString(2));
  28. user.setTokenSecret(cursor.getString(3));
  29. if(!isSimple){
  30. user.setUserName(cursor.getString(4));
  31. ByteArrayInputStreamstream=newByteArrayInputStream(cursor.getBlob(5));
  32. Drawableicon=Drawable.createFromStream(stream,"image");
  33. user.setUserIcon(icon);
  34. }
  35. userList.add(user);
  36. cursor.moveToNext();
  37. }
  38. cursor.close();
  39. returnuserList;
  40. }
  41. //判断users表中的是否包含某个UserID的记录
  42. publicBooleanHaveUserInfo(StringUserId)
  43. {
  44. Booleanb=false;
  45. Cursorcursor=db.query(SqliteHelper.TB_NAME,null,UserInfo.USERID+"="+UserId,null,null,null,null);
  46. b=cursor.moveToFirst();
  47. Log.e("HaveUserInfo",b.toString());
  48. cursor.close();
  49. returnb;
  50. }
  51. //更新users表的记录,根据UserId更新用户昵称和用户图标
  52. publicintUpdateUserInfo(StringuserName,BitmapuserIcon,StringUserId)
  53. {
  54. ContentValuesvalues=newContentValues();
  55. values.put(UserInfo.USERNAME,userName);
  56. //BLOB类型
  57. finalByteArrayOutputStreamos=newByteArrayOutputStream();
  58. //将Bitmap压缩成PNG编码,质量为100%存储
  59. userIcon.compress(Bitmap.CompressFormat.PNG,100,os);
  60. //构造SQLite的Content对象,这里也可以使用raw
  61. values.put(UserInfo.USERICON,os.toByteArray());
  62. intid=db.update(SqliteHelper.TB_NAME,values,UserInfo.USERID+"="+UserId,null);
  63. Log.e("UpdateUserInfo2",id+"");
  64. returnid;
  65. }
  66. //更新users表的记录
  67. publicintUpdateUserInfo(UserInfouser)
  68. {
  69. ContentValuesvalues=newContentValues();
  70. values.put(UserInfo.USERID,user.getUserId());
  71. values.put(UserInfo.TOKEN,user.getToken());
  72. values.put(UserInfo.TOKENSECRET,user.getTokenSecret());
  73. intid=db.update(SqliteHelper.TB_NAME,values,UserInfo.USERID+"="+user.getUserId(),null);
  74. Log.e("UpdateUserInfo",id+"");
  75. returnid;
  76. }
  77. //添加users表的记录
  78. publicLongSaveUserInfo(UserInfouser)
  79. {
  80. ContentValuesvalues=newContentValues();
  81. values.put(UserInfo.USERID,user.getUserId());
  82. values.put(UserInfo.TOKEN,user.getToken());
  83. values.put(UserInfo.TOKENSECRET,user.getTokenSecret());
  84. Longuid=db.insert(SqliteHelper.TB_NAME,UserInfo.ID,values);
  85. Log.e("SaveUserInfo",uid+"");
  86. returnuid;
  87. }
  88. //删除users表的记录
  89. publicintDelUserInfo(StringUserId){
  90. intid=db.delete(SqliteHelper.TB_NAME,UserInfo.USERID+"="+UserId,null);
  91. Log.e("DelUserInfo",id+"");
  92. returnid;
  93. }
  94. }

完成上面的代码后,我们需要在载入页面中调用上面的方法实现sqlite库中是否已经保存有用户的新浪微博的UserID号、Access Token、Access Secret的记录的功能在MainActivity的onCreate方法添加代码:

[java] view plain copy
  1. publicvoidonCreate(BundlesavedInstanceState){
  2. super.onCreate(savedInstanceState);
  3. setContentView(R.layout.main);
  4. ......
  5. //获取账号列表
  6. dbHelper=newDataHelper(this);
  7. List<UserInfo>userList=dbHelper.GetUserList(true);
  8. if(userList.isEmpty())//如果为空说明第一次使用跳到AuthorizeActivity页面进行OAuth认证
  9. {
  10. Intentintent=newIntent();
  11. intent.setClass(MainActivity.this,AuthorizeActivity.class);
  12. startActivity(intent);
  13. }
  14. else//如果不为空读取这些记录的UserID号、AccessToken、AccessSecret值
  15. //然后根据这3个值调用新浪的api接口获取这些记录对应的用户昵称和用户头像图标等信息。
  16. {
  17. for(UserInfouser:userList){
  18. ......
  19. }
  20. }
  21. }

本篇说说关于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开源项目简单了很多。具体代码如下:

[java] view plain copy
  1. publicclassOAuth{
  2. privateCommonsHttpOAuthConsumerhttpOauthConsumer;
  3. privateOAuthProviderhttpOauthprovider;
  4. publicStringconsumerKey;
  5. publicStringconsumerSecret;
  6. publicOAuth()
  7. {
  8. //第一组:(AppKey和AppSecret)
  9. //这组参数就是本系列文本第一篇提到的建一个新的应用获取AppKey和AppSecret。
  10. this("3315495489","e2731e7grf592c0fd7fea32406f86e1b");
  11. }
  12. publicOAuth(StringconsumerKey,StringconsumerSecret)
  13. {
  14. this.consumerKey=consumerKey;
  15. this.consumerSecret=consumerSecret;
  16. }
  17. publicBooleanRequestAccessToken(Activityactivity,StringcallBackUrl){
  18. Booleanret=false;
  19. try{
  20. httpOauthConsumer=newCommonsHttpOAuthConsumer(consumerKey,consumerSecret);
  21. httpOauthprovider=newDefaultOAuthProvider("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");
  22. StringauthUrl=httpOauthprovider.retrieveRequestToken(httpOauthConsumer,callBackUrl);
  23. activity.startActivity(newIntent(Intent.ACTION_VIEW,Uri.parse(authUrl)));
  24. ret=true;
  25. }catch(Exceptione){
  26. }
  27. returnret;
  28. }
  29. publicUserInfoGetAccessToken(Intentintent){
  30. UserInfouser=null;
  31. Uriuri=intent.getData();
  32. Stringverifier=uri.getQueryParameter(oauth.signpost.OAuth.OAUTH_VERIFIER);
  33. try{
  34. httpOauthprovider.setOAuth10a(true);
  35. httpOauthprovider.retrieveAccessToken(httpOauthConsumer,verifier);
  36. }catch(OAuthMessageSignerExceptionex){
  37. ex.printStackTrace();
  38. }catch(OAuthNotAuthorizedExceptionex){
  39. ex.printStackTrace();
  40. }catch(OAuthExpectationFailedExceptionex){
  41. ex.printStackTrace();
  42. }catch(OAuthCommunicationExceptionex){
  43. ex.printStackTrace();
  44. }
  45. SortedSet<String>user_id=httpOauthprovider.getResponseParameters().get("user_id");
  46. StringuserId=user_id.first();
  47. StringuserKey=httpOauthConsumer.getToken();
  48. StringuserSecret=httpOauthConsumer.getTokenSecret();
  49. user=newUserInfo();
  50. user.setUserId(userId);
  51. user.setToken(userKey);
  52. user.setTokenSecret(userSecret);
  53. returnuser;
  54. }
  55. publicHttpResponseSignRequest(Stringtoken,StringtokenSecret,Stringurl,Listparams)
  56. {
  57. HttpPostpost=newHttpPost(url);
  58. //HttpClienthttpClient=null;
  59. try{
  60. post.setEntity(newUrlEncodedFormEntity(params,HTTP.UTF_8));
  61. }catch(UnsupportedEncodingExceptione){
  62. e.printStackTrace();
  63. }
  64. //关闭Expect:100-Continue握手
  65. //100-Continue握手需谨慎使用,因为遇到不支持HTTP/1.1协议的服务器或者代理时会引起问题
  66. post.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE,false);
  67. returnSignRequest(token,tokenSecret,post);
  68. }
  69. publicHttpResponseSignRequest(Stringtoken,StringtokenSecret,HttpPostpost){
  70. httpOauthConsumer=newCommonsHttpOAuthConsumer(consumerKey,consumerSecret);
  71. httpOauthConsumer.setTokenWithSecret(token,tokenSecret);
  72. HttpResponseresponse=null;
  73. try{
  74. httpOauthConsumer.sign(post);
  75. }catch(OAuthMessageSignerExceptione){
  76. e.printStackTrace();
  77. }catch(OAuthExpectationFailedExceptione){
  78. e.printStackTrace();
  79. }catch(OAuthCommunicationExceptione){
  80. e.printStackTrace();
  81. }
  82. //取得HTTPresponse
  83. try{
  84. response=newDefaultHttpClient().execute(post);
  85. }catch(ClientProtocolExceptione){
  86. e.printStackTrace();
  87. }catch(IOExceptione){
  88. e.printStackTrace();
  89. }
  90. returnresponse;
  91. }
  92. }

这样就完成了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:

[java] view plain copy
  1. @Override
  2. publicvoidonCreate(BundlesavedInstanceState){
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.authorize);
  5. .......
  6. }

接下来是本文的重点部分,半透明弹窗用Dialog控件进行实现,首先为这个半透明弹窗新建一个名为dialog.xml的Layout,这个Layout主要是对4个元素进行布局,如图所示分别为i小图标、信息提示、中间文字、开始按钮,首先用LinearLayout对i小图标和信息提示进行水平布局,中间文字以一个TextView跟在下面,对于开始按钮是用RelativeLayout进行底部对齐显示。具体代码如下:

[java] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="wrap_content"
  5. android:layout_height="wrap_content"
  6. android:orientation="vertical"
  7. android:padding="10dip">
  8. <LinearLayout
  9. android:layout_width="wrap_content"
  10. android:layout_height="wrap_content"
  11. android:orientation="horizontal">
  12. <ImageView
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:src="@drawable/info_icon">
  16. </ImageView>
  17. <TextView
  18. android:layout_width="wrap_content"
  19. android:layout_height="wrap_content"
  20. android:text="信息提示"
  21. android:textSize="13px"
  22. android:textColor="#219ac6"
  23. android:layout_marginLeft="5dip">
  24. </TextView>
  25. </LinearLayout>
  26. <TextView
  27. android:id="@+id/text_info"
  28. android:layout_marginTop="6px"
  29. android:layout_width="200px"
  30. android:layout_height="wrap_content"
  31. android:textColor="#686767"
  32. android:textSize="14px"
  33. android:text="第一次使用需要输入您的新浪微博账号和密码进行登录授权">
  34. </TextView>
  35. <RelativeLayout
  36. android:layout_width="fill_parent"
  37. android:layout_height="40px">
  38. <LinearLayout
  39. android:layout_width="wrap_content"
  40. android:layout_height="wrap_content"
  41. android:orientation="horizontal"
  42. android:layout_centerHorizontal="true"
  43. android:layout_alignParentBottom="true">
  44. <ImageButton
  45. android:id="@+id/btn_start"
  46. android:layout_width="80px"
  47. android:layout_height="31px"
  48. android:src="@drawable/btn_start_selector">
  49. </ImageButton>
  50. <ImageButton
  51. android:id="@+id/btn_cancel"
  52. android:layout_width="80px"
  53. android:layout_height="31px"
  54. android:layout_marginLeft="8px"
  55. android:src="@drawable/btn_cancel_selector">
  56. </ImageButton>
  57. </LinearLayout>
  58. </RelativeLayout>
  59. </LinearLayout>

完成了半透明弹窗的Layout定义接下来我们要做的就是为它写一个自定义样式来实现我们想要的显示效果,首先我们需准备一个圆角的半透明png图片名为dia_bg.png并且添加到drawable中,接下来再res/values文件夹新建名为 dialogStyle.xml的resources样式文件,具体代码如下:

[java] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <resources>
  3. <mce:stylename="dialog"parent="@android:style/Theme.Dialog"><!--
  4. <itemname="android:windowFrame">@null</item>
  5. <itemname="android:windowIsFloating">true</item>
  6. <itemname="android:windowIsTranslucent">false</item>
  7. <itemname="android:windowNoTitle">true</item>
  8. <itemname="android:windowBackground">@drawable/dia_bg</item>
  9. <itemname="android:backgroundDimEnabled">false</item>
  10. --></mce:style><stylename="dialog"parent="@android:style/Theme.Dialog"mce_bogus="1"><itemname="android:windowFrame">@null</item>
  11. <itemname="android:windowIsFloating">true</item>
  12. <itemname="android:windowIsTranslucent">false</item>
  13. <itemname="android:windowNoTitle">true</item>
  14. <itemname="android:windowBackground">@drawable/dia_bg</item>
  15. <itemname="android:backgroundDimEnabled">false</item></style>
  16. </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方法添加如下代码:

[java] view plain copy
  1. ......
  2. ViewdiaView=View.inflate(this,R.layout.dialog,null);
  3. dialog=newDialog(AuthorizeActivity.this,R.style.dialog);
  4. dialog.setContentView(diaView);
  5. dialog.show();
  6. ......

最后运行查看效果,到这里我们的任务已经完成了。请关注下一篇功能篇。

android开发我的新浪微博客户端-用户授权页面功能篇(3.2)


在上一篇实现了用户授权页面的UI,如上图,接下来要做的就是在这个基础上完成功能部分真正实现用户的授权认证,这一篇是android开发我的新浪微博客户端-OAuth篇(2.1)的具体应用篇原理就不多解释了不懂的看OAuth篇即可。认证过程从点击开始按钮然后跳转到新浪的授权页面,接着用户在新浪的页面里输入自己的账户和密码确定后返回用户授权页面。首先给开始按钮添加点击事件代码,代码中主要是调用我们前面android开发我的新浪微博客户端-OAuth篇(2.1)完成的OAuth类的RequestAccessToken方法用来获取oauth_verifier,具体代码如下:

[java] view plain copy
  1. ImageButtonstratBtn=(ImageButton)diaView.findViewById(R.id.btn_start);
  2. stratBtn.setOnClickListener(newOnClickListener(){
  3. @Override
  4. publicvoidonClick(Viewarg0){
  5. auth=newOAuth();
  6. auth.RequestAccessToken(AuthorizeActivity.this,CallBackUrl);
  7. }
  8. });

上面的代码中重点来说明一下 RequestAccessToken方法的第二参数CallBackUrl,这个参数是用户在新浪的页面中输入账户密码后完成认证后返回的地址,我这里是这样设置的CallBackUrl = "myapp://AuthorizeActivity",在AndroidManifest.xml中配置给AuthorizeActivity添加如下配置把myapp://AuthorizeActivity指向到AuthorizeActivity,这样当页面返回到AuthorizeActivity中就可以获取到传过来的oauth_verifier参数。

[java] view plain copy
  1. <intent-filter>
  2. <actionandroid:name="android.intent.action.VIEW"/>
  3. <categoryandroid:name="android.intent.category.DEFAULT"/>
  4. <categoryandroid:name="android.intent.category.BROWSABLE"/>
  5. <dataandroid:scheme="myapp"android:host="AuthorizeActivity"/>
  6. </intent-filter>

再AuthorizeActivity如果来接收返回的oauth_verifier参数呢?接下来在AuthorizeActivity添加如下方法:

[java] view plain copy
  1. @Override
  2. protectedvoidonNewIntent(Intentintent){
  3. super.onNewIntent(intent);
  4. //在这里处理获取返回的oauth_verifier参数
  5. }

关于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添加如下代码:

[java] view plain copy
  1. UserInfouser=auth.GetAccessToken(intent);
  2. if(user!=null){
  3. DataHelperhelper=newDataHelper(this);
  4. Stringuid=user.getUserId();
  5. if(helper.HaveUserInfo(uid))
  6. {
  7. helper.UpdateUserInfo(user);
  8. Log.e("UserInfo","update");
  9. }else
  10. {
  11. helper.SaveUserInfo(user);
  12. Log.e("UserInfo","add");
  13. }
  14. }

通过上面的代码完成了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,然后根据页面显示要求编写如下的布局控制:

[java] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:id="@+id/layout"
  5. android:orientation="vertical"
  6. android:layout_width="fill_parent"
  7. android:layout_height="fill_parent">
  8. <ImageView
  9. android:layout_width="wrap_content"
  10. android:layout_height="wrap_content"
  11. android:src="@drawable/logo_s"
  12. android:layout_marginTop="5dip"
  13. android:layout_marginLeft="5dip">
  14. </ImageView>
  15. <RelativeLayout
  16. android:layout_width="fill_parent"
  17. android:layout_height="fill_parent">
  18. <RelativeLayout
  19. android:id="@+id/iconBtn"
  20. android:layout_width="90px"
  21. android:layout_height="80px"
  22. android:background="@drawable/icon_selector"
  23. android:layout_above="@+id/selectLayout"
  24. android:layout_centerHorizontal="true"
  25. android:layout_marginBottom="20dip">
  26. <ImageView
  27. android:id="@+id/icon"
  28. android:layout_width="wrap_content"
  29. android:layout_height="wrap_content"
  30. android:layout_centerInParent="true">
  31. </ImageView>
  32. </RelativeLayout>
  33. <RelativeLayout
  34. android:id="@+id/selectLayout"
  35. android:layout_width="wrap_content"
  36. android:layout_height="wrap_content"
  37. android:layout_centerInParent="true">
  38. <EditText
  39. android:id="@+id/iconSelect"
  40. android:layout_width="200px"
  41. android:layout_height="wrap_content"
  42. android:maxLength="10"
  43. android:paddingLeft="20px"
  44. android:editable="false"
  45. android:enabled="false"
  46. android:textSize="13px"
  47. android:background="@drawable/input_over">
  48. </EditText>
  49. <ImageButton
  50. android:id="@+id/iconSelectBtn"
  51. android:layout_width="wrap_content"
  52. android:layout_height="wrap_content"
  53. android:layout_marginRight="1.0dip"
  54. android:layout_alignTop="@+id/iconSelect"
  55. android:layout_alignRight="@+id/iconSelect"
  56. android:layout_alignBottom="@+id/iconSelect"
  57. android:background="@drawable/more_selector">
  58. </ImageButton>
  59. <ImageButton
  60. android:id="@+id/login"
  61. android:layout_width="40px"
  62. android:layout_height="40px"
  63. android:layout_marginLeft="5dip"
  64. android:layout_alignTop="@+id/iconSelectBtn"
  65. android:layout_toRightOf="@+id/iconSelectBtn"
  66. android:layout_alignBottom="@+id/iconSelectBtn"
  67. android:background="@drawable/btn_in_selector">
  68. </ImageButton>
  69. </RelativeLayout>
  70. <RelativeLayout
  71. android:layout_width="fill_parent"
  72. android:layout_height="44dip"
  73. android:layout_alignParentBottom="true"
  74. android:background="#BB768e95">
  75. <LinearLayout
  76. android:id="@+id/addLayout"
  77. android:layout_width="wrap_content"
  78. android:layout_height="wrap_content"
  79. android:orientation="vertical"
  80. android:layout_alignParentLeft="true"
  81. android:gravity="center"
  82. android:layout_marginTop="3px">
  83. <ImageButton
  84. android:id="@+id/addIcon"
  85. android:layout_width="wrap_content"
  86. android:layout_height="wrap_content"
  87. android:background="@drawable/add_selector">
  88. </ImageButton>
  89. <TextView
  90. android:layout_width="wrap_content"
  91. android:layout_height="wrap_content"
  92. android:textColor="#ffffff"
  93. android:textSize="12px"
  94. android:text="添加账号">
  95. </TextView>
  96. </LinearLayout>
  97. <LinearLayout
  98. android:id="@+id/exitLayout"
  99. android:layout_width="wrap_content"
  100. android:layout_height="wrap_content"
  101. android:orientation="vertical"
  102. android:layout_centerInParent="true"
  103. android:gravity="center"
  104. android:layout_marginTop="3px">
  105. <ImageButton
  106. android:id="@+id/exitIcon"
  107. android:layout_width="wrap_content"
  108. android:layout_height="wrap_content"
  109. android:background="@drawable/exit_selector">
  110. </ImageButton>
  111. <TextView
  112. android:layout_width="wrap_content"
  113. android:layout_height="wrap_content"
  114. android:textColor="#ffffff"
  115. android:textSize="12px"
  116. android:text="退出软件">
  117. </TextView>
  118. </LinearLayout>
  119. <LinearLayout
  120. android:id="@+id/delLayout"
  121. android:layout_width="wrap_content"
  122. android:layout_height="wrap_content"
  123. android:orientation="vertical"
  124. android:layout_alignParentRight="true"
  125. android:gravity="center"
  126. android:layout_marginTop="3px">
  127. <ImageButton
  128. android:id="@+id/delIcon"
  129. android:layout_width="wrap_content"
  130. android:layout_height="wrap_content"
  131. android:background="@drawable/del_selector">
  132. </ImageButton>
  133. <TextView
  134. android:layout_width="wrap_content"
  135. android:layout_height="wrap_content"
  136. android:textColor="#ffffff"
  137. android:textSize="12px"
  138. android:text="删除账号">
  139. </TextView>
  140. </LinearLayout>
  141. </RelativeLayout>
  142. </RelativeLayout>
  143. </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的样式,具体解释可以参考前面的户授权验证页面功能,内容如下:

[java] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <resources>
  3. <mce:stylename="dialog2"parent="@android:style/Theme.Dialog"><!--
  4. <itemname="android:windowFrame">@null</item>
  5. <itemname="android:windowIsFloating">true</item>
  6. <itemname="android:windowIsTranslucent">false</item>
  7. <itemname="android:windowNoTitle">true</item>
  8. <itemname="android:windowBackground">@drawable/mask_bg</item>
  9. <itemname="android:backgroundDimEnabled">true</item>
  10. --></mce:style><stylename="dialog2"parent="@android:style/Theme.Dialog"mce_bogus="1"><itemname="android:windowFrame">@null</item>
  11. <itemname="android:windowIsFloating">true</item>
  12. <itemname="android:windowIsTranslucent">false</item>
  13. <itemname="android:windowNoTitle">true</item>
  14. <itemname="android:windowBackground">@drawable/mask_bg</item>
  15. <itemname="android:backgroundDimEnabled">true</item></style>
  16. </resources>

接下来还需要定义选择列表的layout,新建名为dialog2.xml的layout文件,内容如下:

[java] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="wrap_content"
  5. android:layout_height="wrap_content"
  6. android:orientation="vertical"
  7. android:padding="4dip">
  8. <ListView
  9. android:id="@+id/list"
  10. android:layout_width="240px"
  11. android:layout_height="220px"
  12. android:divider="#f1f2f2"
  13. android:dividerHeight="1px"
  14. android:layout_margin="5px"
  15. android:background="#ffffff"
  16. android:cacheColorHint="#00000000">
  17. </ListView>
  18. </LinearLayout>

完成了layout和样式文件的编写,接下来就是把dialogStyles2.xml样式文件和dialog2.xml的列表layout用起来,当点击id为iconSelectBtn的ImageButton时显示用户选择窗口,在LoginActivity的onCreate方法中添加如下代码:

[java] view plain copy
  1. publicvoidonCreate(BundlesavedInstanceState){
  2. super.onCreate(savedInstanceState);
  3. setContentView(R.layout.login);
  4. LinearLayoutlayout=(LinearLayout)findViewById(R.id.layout);
  5. //背景自动适应
  6. AndroidHelper.AutoBackground(this,layout,R.drawable.bg_v,R.drawable.bg_h);
  7. ImageButtoniconSelectBtn=(ImageButton)findViewById(R.id.iconSelectBtn);
  8. iconSelectBtn.setOnClickListener(newOnClickListener(){
  9. @Override
  10. publicvoidonClick(Viewv){
  11. ViewdiaView=View.inflate(LoginActivity.this,R.layout.dialog2,null);
  12. dialog=newDialog(LoginActivity.this,R.style.dialog2);
  13. dialog.setContentView(diaView);
  14. dialog.show();
  15. ......
  16. }
  17. });

到这里登录的UI部分就实现的差不多了,剩下的都是一些功能部分代码用来实现从sqlite中账号列表的获取,以及点击选择等交互操作等,这些在下一篇中来继续的讲。

android开发我的新浪微博客户端-登录页面功能篇(4.2)

上一篇中完成了如上图的UI部分的实现,现在继续来讲功能的实现,用户登录操作主要就是账号列表显示和选择账号登录两个功能其他的都是些简单的辅助功能,首先是点击id为iconSelectBtn的ImageButton时显示用户选择窗口,这个时候去数据库中获取账号记录然后在选择窗口中以列表方式显示出来,通过上一篇已经知道Id为list的ListView控件来显示账号列表,首先是从数据库中获取所有的账户记录然后设置默认选中的用户账号代码如下:

[java] view plain copy
  1. privatevoidinitUser(){
  2. //获取账号列表
  3. dbHelper=newDataHelper(this);
  4. userList=dbHelper.GetUserList(false);
  5. if(userList.isEmpty())
  6. {
  7. Intentintent=newIntent();
  8. intent.setClass(LoginActivity.this,AuthorizeActivity.class);
  9. startActivity(intent);
  10. }
  11. else
  12. {
  13. SharedPreferencespreferences=getSharedPreferences(Select_Name,Activity.MODE_PRIVATE);
  14. Stringstr=preferences.getString("name","");
  15. UserInfouser=null;
  16. if(str!="")
  17. {
  18. user=GetUserByName(str);
  19. }
  20. if(user==null)
  21. {
  22. user=userList.get(0);
  23. }
  24. icon.setImageDrawable(user.getUserIcon());
  25. iconSelect.setText(user.getUserName());
  26. }
  27. }

这个initUser() 初始账号的方法在LoginActivity的onCreate中调用,主要完成两件事情,第一件获取通过userList = dbHelper.GetUserList(false);获取所有的账户记录,关于DataHelper前面已经有说过了,如果获取的用户记录为空那么就跳转到用户授权功能页面让用户添加账号,如果不为空那么通过SharedPreferences去读取用户上一次选择的账号名称,如果没有或者数据库里账号记录不包括这个账户名称那么默认显示记录的第一个账号和头像,如果有那么显示这个账户的名称和头像。关于SharedPreferences,是android提供给开发者用来存储一些简单的数据用的,非常方便类似于网站的Cookie,在这里我就是用这个来保存上一次用户选择的是哪个账号,非常实用。
接下类首先为Id为list的ListView控件准备数据Adapter,这个Adapter非常简单就是普通的adapter继承BaseAdapter即可,代码如下:

[java] view plain copy
  1. publicclassUserAdapaterextendsBaseAdapter{
  2. @Override
  3. publicintgetCount(){
  4. returnuserList.size();
  5. }
  6. @Override
  7. publicObjectgetItem(intposition){
  8. returnuserList.get(position);
  9. }
  10. @Override
  11. publiclonggetItemId(intposition){
  12. returnposition;
  13. }
  14. @Override
  15. publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
  16. convertView=LayoutInflater.from(getApplicationContext()).inflate(R.layout.item_user,null);
  17. ImageViewiv=(ImageView)convertView.findViewById(R.id.iconImg);
  18. TextViewtv=(TextView)convertView.findViewById(R.id.showName);
  19. UserInfouser=userList.get(position);
  20. try{
  21. //设置图片显示
  22. iv.setImageDrawable(user.getUserIcon());
  23. //设置信息
  24. tv.setText(user.getUserName());
  25. }catch(Exceptione){
  26. e.printStackTrace();
  27. }
  28. returnconvertView;
  29. }

接下就是为这个ListView设定数据源Adapter,在账号选择窗口显示的时候进行设置,添加到id为iconSelectBtn的ImageButton的OnClickListener中代码如下:

[java] view plain copy
  1. ImageButtoniconSelectBtn=(ImageButton)findViewById(R.id.iconSelectBtn);
  2. iconSelectBtn.setOnClickListener(newOnClickListener(){
  3. @Override
  4. publicvoidonClick(Viewv){
  5. ......
  6. dialog.show();
  7. UserAdapateradapater=newUserAdapater();
  8. ListViewlistview=(ListView)diaView.findViewById(R.id.list);
  9. listview.setVerticalScrollBarEnabled(false);//ListView去掉下拉条
  10. listview.setAdapter(adapater);
  11. listview.setOnItemClickListener(newOnItemClickListener(){
  12. @Override
  13. publicvoidonItemClick(AdapterView<?>arg0,Viewview,intarg2,longarg3){
  14. TextViewtv=(TextView)view.findViewById(R.id.showName);
  15. iconSelect.setText(tv.getText());
  16. ImageViewiv=(ImageView)view.findViewById(R.id.iconImg);
  17. icon.setImageDrawable(iv.getDrawable());
  18. dialog.dismiss();
  19. }
  20. });
  21. }
  22. });

通过上面代码完成了账号选择的功能,接下来给id为login的ImageButton添加OnClickListener,使得点击后以当前选择账号进入微博首页,代码如下:

[java] view plain copy
  1. @Override
  2. publicvoidonCreate(BundlesavedInstanceState){
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.login);
  5. ......
  6. ImageButtonlogin=(ImageButton)findViewById(R.id.login);
  7. login.setOnClickListener(newOnClickListener(){
  8. @Override
  9. publicvoidonClick(Viewv){
  10. GoHome();
  11. }
  12. });
  13. }
  14. //进入用户首页
  15. privatevoidGoHome(){
  16. if(userList!=null)
  17. {
  18. Stringname=iconSelect.getText().toString();
  19. UserInfou=GetUserByName(name);
  20. if(u!=null)
  21. {
  22. ConfigHelper.nowUser=u;//获取当前选择的用户并且保存
  23. }
  24. }
  25. if(ConfigHelper.nowUser!=null)
  26. {
  27. //进入用户首页
  28. Intentintent=newIntent();
  29. intent.setClass(LoginActivity.this,HomeActivity.class);
  30. startActivity(intent);
  31. }
  32. }

在上面的GoHome方法中ConfigHelper.nowUser是类型为UserInfo的static类型用来保存当前登录账号的信息,替代web中session使用。
最后添加如下方法,用来当这个登录LoginActivity结束的时候保存当前选择的账户名称到SharedPreferences中,以便帮用户记住登录账号的功能,就是前面的initUser() 初始账号的方法中会获取保存在SharedPreferences中的账户名称,代码如下:

[java] view plain copy
  1. @Override
  2. protectedvoidonStop(){
  3. //获得SharedPreferences对象
  4. SharedPreferencesMyPreferences=getSharedPreferences(Select_Name,Activity.MODE_PRIVATE);
  5. //获得SharedPreferences.Editor对象
  6. SharedPreferences.Editoreditor=MyPreferences.edit();
  7. //保存组件中的值
  8. editor.putString("name",iconSelect.getText().toString());
  9. editor.commit();
  10. super.onStop();
  11. }

至此登录页面功能篇结束,请继续关注下一篇。

android开发我的新浪微博客户端-用户首页面UI篇(5.1)

在前篇完成了用户登录功能后开始用户首页的开发,用户的首页主要的内容是当前登录用户关注的微博列表,本篇先来讲讲UI的实现,效果如上图,整个页面分为上、中、下三部分,上面部分是工具条,显示当前登录用户的昵称以及写微博、刷新两个功能按钮;中间部分是当前用户关注的最新微博列表,下面部分是功能切换栏,用来进行各个功能之间的切换。

首先新建名为HomeActivity.java的Activity作为用户首页,然后在res/layout目录下新建名为home.xml的Layout,具体代码如下:

[java] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:id="@+id/layout"
  5. android:orientation="vertical"
  6. android:layout_width="fill_parent"
  7. android:layout_height="fill_parent">
  8. <RelativeLayout
  9. android:layout_width="fill_parent"
  10. android:layout_height="wrap_content"
  11. android:layout_margin="3px">
  12. <ImageView
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:src="@drawable/logo_ss">
  16. </ImageView>
  17. <TextView
  18. android:id="@+id/showName"
  19. android:layout_width="wrap_content"
  20. android:layout_height="wrap_content"
  21. android:layout_centerInParent="true"
  22. android:textColor="#343434"
  23. android:textSize="15px">
  24. </TextView>
  25. <ImageButton
  26. android:id="@+id/writeBtn"
  27. android:layout_width="wrap_content"
  28. android:layout_height="wrap_content"
  29. android:layout_toLeftOf="@+id/refreshBtn"
  30. android:background="@drawable/btn_write_selector">
  31. </ImageButton>
  32. <ImageButton
  33. android:id="@+id/refreshBtn"
  34. android:layout_width="wrap_content"
  35. android:layout_height="wrap_content"
  36. android:layout_alignParentRight="true"
  37. android:layout_marginLeft="12px"
  38. android:background="@drawable/btn_refresh_selector">
  39. </ImageButton>
  40. </RelativeLayout>
  41. <LinearLayout
  42. android:layout_width="fill_parent"
  43. android:layout_height="wrap_content"
  44. android:background="@drawable/hr">
  45. </LinearLayout>
  46. <RelativeLayout
  47. android:layout_width="fill_parent"
  48. android:layout_height="fill_parent">
  49. <ListView
  50. android:id="@+id/Msglist"
  51. android:layout_width="fill_parent"
  52. android:layout_height="match_parent"
  53. android:divider="@drawable/divider"
  54. android:dividerHeight="2px"
  55. android:layout_margin="0px"
  56. android:background="#BBFFFFFF"
  57. android:cacheColorHint="#00000000"
  58. android:layout_above="@+id/toolbarLayout"
  59. android:fastScrollEnabled="true"
  60. android:focusable="true">
  61. </ListView>
  62. <LinearLayout
  63. android:id="@+id/loadingLayout"
  64. android:layout_width="wrap_content"
  65. android:layout_height="wrap_content"
  66. android:orientation="vertical"
  67. android:visibility="invisible"
  68. android:layout_centerInParent="true">
  69. <ProgressBar
  70. android:id="@+id/loading"
  71. android:layout_width="31px"
  72. android:layout_height="31px"
  73. android:layout_gravity="center"
  74. style="@style/progressStyle">
  75. </ProgressBar>
  76. <TextView
  77. android:layout_width="wrap_content"
  78. android:layout_height="wrap_content"
  79. android:text="正在载入"
  80. android:textSize="12px"
  81. android:textColor="#9c9c9c"
  82. android:layout_gravity="center"
  83. android:layout_below="@+id/loading">
  84. </TextView>
  85. </LinearLayout>
  86. <LinearLayout
  87. android:id="@+id/toolbarLayout"
  88. android:layout_width="fill_parent"
  89. android:layout_height="44dip"
  90. android:layout_alignParentBottom="true">
  91. </LinearLayout>
  92. </RelativeLayout>
  93. </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,内容如下:

[java] view plain copy
  1. <?xmlversion="1.0"encoding="UTF-8"?>
  2. <resources>
  3. <mce:stylename="progressStyle"width="38"height="38"parent="@android:style/Widget.ProgressBar.Small"><!--
  4. <itemname="android:indeterminateDrawable">@anim/loading</item>
  5. --></mce:style><stylename="progressStyle"width="38"height="38"parent="@android:style/Widget.ProgressBar.Small"mce_bogus="1"><itemname="android:indeterminateDrawable">@anim/loading</item></style>
  6. </resources>

接着准备好r1.png - r8.png,

八张不同的小图片分别代表每旋转45度图片,八张刚好是360度。把这些图片添加到res/drawable-mdpi目录中。然后在res/anim目录下新建名为loading.xml动画文件,内容如下:

[java] view plain copy
  1. <?xmlversion="1.0"encoding="UTF-8"?>
  2. <animation-listandroid:oneshot="false"
  3. xmlns:android="http://schemas.android.com/apk/res/android">
  4. <itemandroid:duration="200"android:drawable="@drawable/r1"/>
  5. <itemandroid:duration="200"android:drawable="@drawable/r2"/>
  6. <itemandroid:duration="200"android:drawable="@drawable/r3"/>
  7. <itemandroid:duration="200"android:drawable="@drawable/r4"/>
  8. <itemandroid:duration="200"android:drawable="@drawable/r5"/>
  9. <itemandroid:duration="200"android:drawable="@drawable/r6"/>
  10. <itemandroid:duration="200"android:drawable="@drawable/r7"/>
  11. <itemandroid:duration="200"android:drawable="@drawable/r8"/>
  12. </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实体类,代码如下:

[java] view plain copy
  1. publicclassWeiBoInfo{
  2. //文章id
  3. privateStringid;
  4. publicStringgetId(){
  5. returnid;
  6. }
  7. publicvoidsetId(Stringid){
  8. this.id=id;
  9. }
  10. //发布人id
  11. privateStringuserId;
  12. publicStringgetUserId(){
  13. returnuserId;
  14. }
  15. publicvoidsetUserId(StringuserId){
  16. this.userId=userId;
  17. }
  18. //发布人名字
  19. privateStringuserName;
  20. publicStringgetUserName(){
  21. returnuserName;
  22. }
  23. publicvoidsetUserName(StringuserName){
  24. this.userName=userName;
  25. }
  26. //发布人头像
  27. privateStringuserIcon;
  28. publicStringgetUserIcon(){
  29. returnuserIcon;
  30. }
  31. publicvoidsetUserIcon(StringuserIcon){
  32. this.userIcon=userIcon;
  33. }
  34. //发布时间
  35. privateStringtime;
  36. publicStringgetTime(){
  37. returntime;
  38. }
  39. publicvoidsetTime(Stringtime)
  40. {
  41. this.time=time;
  42. }
  43. //是否有图片
  44. privateBooleanhaveImage=false;
  45. publicBooleangetHaveImage(){
  46. returnhaveImage;
  47. }
  48. publicvoidsetHaveImage(BooleanhaveImage){
  49. this.haveImage=haveImage;
  50. }
  51. //文章内容
  52. privateStringtext;
  53. publicStringgetText(){
  54. returntext;
  55. }
  56. publicvoidsetText(Stringtext){
  57. this.text=text;
  58. }
  59. }

然后在res/layout目录下新建名为weibo.xml的Layout用来控制ListView子项的显示部件,代码很简单不多解释了,直接看下面代码:

[java] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="wrap_content"
  5. android:layout_height="wrap_content"
  6. android:orientation="horizontal">
  7. <ImageView
  8. android:id="@+id/wbicon"
  9. android:layout_width="wrap_content"
  10. android:layout_height="wrap_content"
  11. android:src="@drawable/usericon"
  12. android:layout_margin="8px">
  13. </ImageView>
  14. <LinearLayout
  15. android:layout_width="fill_parent"
  16. android:layout_height="wrap_content"
  17. android:orientation="vertical"
  18. android:paddingLeft="0px"
  19. android:paddingRight="5px"
  20. android:layout_marginTop="5px"
  21. android:layout_marginBottom="5px">
  22. <RelativeLayout
  23. android:layout_width="fill_parent"
  24. android:layout_height="wrap_content">
  25. <TextView
  26. android:id="@+id/wbuser"
  27. android:layout_width="wrap_content"
  28. android:layout_height="wrap_content"
  29. android:textSize="15px"
  30. android:textColor="#424952"
  31. android:layout_alignParentLeft="true">
  32. </TextView>
  33. <ImageView
  34. android:id="@+id/wbimage"
  35. android:layout_width="wrap_content"
  36. android:layout_height="wrap_content"
  37. android:layout_marginTop="3px"
  38. android:layout_marginRight="5px"
  39. android:layout_toLeftOf="@+id/wbtime">
  40. </ImageView>
  41. <TextView
  42. android:id="@+id/wbtime"
  43. android:layout_width="wrap_content"
  44. android:layout_height="wrap_content"
  45. android:layout_alignParentRight="true"
  46. android:textColor="#f7a200"
  47. android:textSize="12px">
  48. </TextView>
  49. </RelativeLayout>
  50. <TextView
  51. android:id="@+id/wbtext"
  52. android:layout_width="wrap_content"
  53. android:layout_height="wrap_content"
  54. android:textColor="#424952"
  55. android:textSize="13px"
  56. android:layout_marginTop="4px">
  57. </TextView>
  58. </LinearLayout>
  59. </LinearLayout>

接下来为列表控件定义一个数据Adapter,代码如下:

[java] view plain copy
  1. privateList<WeiBoInfo>wbList;
  2. //微博列表Adapater
  3. publicclassWeiBoAdapaterextendsBaseAdapter{
  4. privateAsyncImageLoaderasyncImageLoader;
  5. @Override
  6. publicintgetCount(){
  7. returnwbList.size();
  8. }
  9. @Override
  10. publicObjectgetItem(intposition){
  11. returnwbList.get(position);
  12. }
  13. @Override
  14. publiclonggetItemId(intposition){
  15. returnposition;
  16. }
  17. @Override
  18. publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
  19. asyncImageLoader=newAsyncImageLoader();
  20. convertView=LayoutInflater.from(getApplicationContext()).inflate(R.layout.weibo,null);
  21. WeiBoHolderwh=newWeiBoHolder();
  22. wh.wbicon=(ImageView)convertView.findViewById(R.id.wbicon);
  23. wh.wbtext=(TextView)convertView.findViewById(R.id.wbtext);
  24. wh.wbtime=(TextView)convertView.findViewById(R.id.wbtime);
  25. wh.wbuser=(TextView)convertView.findViewById(R.id.wbuser);
  26. wh.wbimage=(ImageView)convertView.findViewById(R.id.wbimage);
  27. WeiBoInfowb=wbList.get(position);
  28. if(wb!=null){
  29. convertView.setTag(wb.getId());
  30. wh.wbuser.setText(wb.getUserName());
  31. wh.wbtime.setText(wb.getTime());
  32. wh.wbtext.setText(wb.getText(),TextView.BufferType.SPANNABLE);
  33. textHighlight(wh.wbtext,newchar[]{'#'},newchar[]{'#'});
  34. textHighlight(wh.wbtext,newchar[]{'@'},newchar[]{':',''});
  35. textHighlight2(wh.wbtext,"http://","");
  36. if(wb.getHaveImage()){
  37. wh.wbimage.setImageResource(R.drawable.images);
  38. }
  39. DrawablecachedImage=asyncImageLoader.loadDrawable(wb.getUserIcon(),wh.wbicon,newImageCallback(){
  40. @Override
  41. publicvoidimageLoaded(DrawableimageDrawable,ImageViewimageView,StringimageUrl){
  42. imageView.setImageDrawable(imageDrawable);
  43. }
  44. });
  45. if(cachedImage==null){
  46. wh.wbicon.setImageResource(R.drawable.usericon);
  47. }else{
  48. wh.wbicon.setImageDrawable(cachedImage);
  49. }
  50. }
  51. returnconvertView;
  52. }

上面的这个Adapter实现没有什么特别的很普通,不过这个中使用了AsyncImageLoader的方法,这个是用来实现用户头像图标的异步载入显示,这样能提高列表显示的速度,提高用户体验,AsyncImageLoader的代码如下:

[java] view plain copy
  1. publicclassAsyncImageLoader{
  2. //SoftReference是软引用,是为了更好的为了系统回收变量
  3. privateHashMap<String,SoftReference<Drawable>>imageCache;
  4. publicAsyncImageLoader(){
  5. imageCache=newHashMap<String,SoftReference<Drawable>>();
  6. }
  7. publicDrawableloadDrawable(finalStringimageUrl,finalImageViewimageView,finalImageCallbackimageCallback){
  8. if(imageCache.containsKey(imageUrl)){
  9. //从缓存中获取
  10. SoftReference<Drawable>softReference=imageCache.get(imageUrl);
  11. Drawabledrawable=softReference.get();
  12. if(drawable!=null){
  13. returndrawable;
  14. }
  15. }
  16. finalHandlerhandler=newHandler(){
  17. publicvoidhandleMessage(Messagemessage){
  18. imageCallback.imageLoaded((Drawable)message.obj,imageView,imageUrl);
  19. }
  20. };
  21. //建立新一个新的线程下载图片
  22. newThread(){
  23. @Override
  24. publicvoidrun(){
  25. Drawabledrawable=loadImageFromUrl(imageUrl);
  26. imageCache.put(imageUrl,newSoftReference<Drawable>(drawable));
  27. Messagemessage=handler.obtainMessage(0,drawable);
  28. handler.sendMessage(message);
  29. }
  30. }.start();
  31. returnnull;
  32. }
  33. publicstaticDrawableloadImageFromUrl(Stringurl){
  34. URLm;
  35. InputStreami=null;
  36. try{
  37. m=newURL(url);
  38. i=(InputStream)m.getContent();
  39. }catch(MalformedURLExceptione1){
  40. e1.printStackTrace();
  41. }catch(IOExceptione){
  42. e.printStackTrace();
  43. }
  44. Drawabled=Drawable.createFromStream(i,"src");
  45. returnd;
  46. }
  47. //回调接口
  48. publicinterfaceImageCallback{
  49. publicvoidimageLoaded(DrawableimageDrawable,ImageViewimageView,StringimageUrl);
  50. }
  51. }

完成上述的工作后,接下来就是显示微薄列表, 在HomeActivity的onCreate方法中调用loadList();代码如下:

[java] view plain copy
  1. @Override
  2. publicvoidonCreate(BundlesavedInstanceState){
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.home);
  5. 。。。。。。
  6. loadList();
  7. }
  8. privatevoidloadList(){
  9. if(ConfigHelper.nowUser==null)
  10. {
  11. }
  12. else
  13. {
  14. user=ConfigHelper.nowUser;
  15. //显示当前用户名称
  16. TextViewshowName=(TextView)findViewById(R.id.showName);
  17. showName.setText(user.getUserName());
  18. OAuthauth=newOAuth();
  19. Stringurl="http://api.t.sina.com.cn/statuses/friends_timeline.json";
  20. Listparams=newArrayList();
  21. params.add(newBasicNameValuePair("source",auth.consumerKey));
  22. HttpResponseresponse=auth.SignRequest(user.getToken(),user.getTokenSecret(),url,params);
  23. if(200==response.getStatusLine().getStatusCode()){
  24. try{
  25. InputStreamis=response.getEntity().getContent();
  26. Readerreader=newBufferedReader(newInputStreamReader(is),4000);
  27. StringBuilderbuffer=newStringBuilder((int)response.getEntity().getContentLength());
  28. try{
  29. char[]tmp=newchar[1024];
  30. intl;
  31. while((l=reader.read(tmp))!=-1){
  32. buffer.append(tmp,0,l);
  33. }
  34. }finally{
  35. reader.close();
  36. }
  37. Stringstring=buffer.toString();
  38. //Log.e("json","rs:"+string);
  39. response.getEntity().consumeContent();
  40. JSONArraydata=newJSONArray(string);
  41. for(inti=0;i<data.length();i++)
  42. {
  43. JSONObjectd=data.getJSONObject(i);
  44. //Log.e("json","rs:"+d.getString("created_at"));
  45. if(d!=null){
  46. JSONObjectu=d.getJSONObject("user");
  47. if(d.has("retweeted_status")){
  48. JSONObjectr=d.getJSONObject("retweeted_status");
  49. }
  50. //微博id
  51. Stringid=d.getString("id");
  52. StringuserId=u.getString("id");
  53. StringuserName=u.getString("screen_name");
  54. StringuserIcon=u.getString("profile_image_url");
  55. Log.e("userIcon",userIcon);
  56. Stringtime=d.getString("created_at");
  57. Stringtext=d.getString("text");
  58. BooleanhaveImg=false;
  59. if(d.has("thumbnail_pic")){
  60. haveImg=true;
  61. //Stringthumbnail_pic=d.getString("thumbnail_pic");
  62. //Log.e("thumbnail_pic",thumbnail_pic);
  63. }
  64. Datedate=newDate(time);
  65. time=ConvertTime(date);
  66. if(wbList==null){
  67. wbList=newArrayList<WeiBoInfo>();
  68. }
  69. WeiBoInfow=newWeiBoInfo();
  70. w.setId(id);
  71. w.setUserId(userId);
  72. w.setUserName(userName);
  73. w.setTime(time);
  74. w.setText(text);
  75. w.setHaveImage(haveImg);
  76. w.setUserIcon(userIcon);
  77. wbList.add(w);
  78. }
  79. }
  80. }catch(IllegalStateExceptione){
  81. e.printStackTrace();
  82. }catch(IOExceptione){
  83. e.printStackTrace();
  84. }catch(JSONExceptione){
  85. e.printStackTrace();
  86. }
  87. }
  88. if(wbList!=null)
  89. {
  90. WeiBoAdapateradapater=newWeiBoAdapater();
  91. ListViewMsglist=(ListView)findViewById(R.id.Msglist);
  92. Msglist.setOnItemClickListener(newOnItemClickListener(){
  93. @Override
  94. publicvoidonItemClick(AdapterView<?>arg0,Viewview,intarg2,longarg3){
  95. Objectobj=view.getTag();
  96. if(obj!=null){
  97. Stringid=obj.toString();
  98. Intentintent=newIntent(HomeActivity.this,ViewActivity.class);
  99. Bundleb=newBundle();
  100. b.putString("key",id);
  101. intent.putExtras(b);
  102. startActivity(intent);
  103. }
  104. }
  105. });
  106. Msglist.setAdapter(adapater);
  107. }
  108. }
  109. loadingLayout.setVisibility(View.GONE);
  110. }

上面的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,代码如下:

[java] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:id="@+id/layout"
  5. android:orientation="vertical"
  6. android:layout_width="fill_parent"
  7. android:layout_height="fill_parent">
  8. <RelativeLayout
  9. android:layout_width="fill_parent"
  10. android:layout_height="wrap_content"
  11. android:layout_margin="3px">
  12. <ImageView
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:src="@drawable/logo_ss">
  16. </ImageView>
  17. <TextView
  18. android:id="@+id/showName"
  19. android:layout_width="wrap_content"
  20. android:layout_height="wrap_content"
  21. android:layout_centerInParent="true"
  22. android:textColor="#343434"
  23. android:text="阅读微博"
  24. android:textSize="16px">
  25. </TextView>
  26. <ImageButton
  27. android:id="@+id/returnBtn"
  28. android:layout_width="wrap_content"
  29. android:layout_height="wrap_content"
  30. android:layout_toLeftOf="@+id/homeBtn"
  31. android:background="@drawable/bnt_return_selector">
  32. </ImageButton>
  33. <ImageButton
  34. android:id="@+id/homeBtn"
  35. android:layout_width="wrap_content"
  36. android:layout_height="wrap_content"
  37. android:layout_alignParentRight="true"
  38. android:layout_marginLeft="12px"
  39. android:background="@drawable/btn_home_selector">
  40. </ImageButton>
  41. </RelativeLayout>
  42. <LinearLayout
  43. android:layout_width="fill_parent"
  44. android:layout_height="wrap_content"
  45. android:background="@drawable/hr">
  46. </LinearLayout>
  47. <RelativeLayout
  48. android:id="@+id/user_bg"
  49. android:layout_width="fill_parent"
  50. android:layout_height="78px"
  51. android:paddingTop="8px"
  52. android:paddingLeft="15px"
  53. android:background="@drawable/u_bg_v">
  54. <ImageView
  55. android:id="@+id/user_icon"
  56. android:layout_width="wrap_content"
  57. android:layout_height="wrap_content"
  58. android:layout_alignParentLeft="true"
  59. android:src="@drawable/usericon">
  60. </ImageView>
  61. <TextView
  62. android:id="@+id/user_name"
  63. android:layout_width="wrap_content"
  64. android:layout_height="wrap_content"
  65. android:layout_toRightOf="@+id/user_icon"
  66. android:layout_marginLeft="10px"
  67. android:layout_marginTop="18px"
  68. android:textColor="#000000">
  69. </TextView>
  70. <ImageView
  71. android:layout_width="wrap_content"
  72. android:layout_height="wrap_content"
  73. android:layout_alignParentRight="true"
  74. android:layout_marginRight="5px"
  75. android:layout_marginTop="10px"
  76. android:src="@drawable/sjjt">
  77. </ImageView>
  78. </RelativeLayout>
  79. <RelativeLayout
  80. android:layout_width="fill_parent"
  81. android:layout_height="fill_parent">
  82. <ScrollView
  83. android:layout_width="fill_parent"
  84. android:layout_height="fill_parent"
  85. android:paddingLeft="17px"
  86. android:paddingRight="17px"
  87. android:paddingBottom="5px"
  88. android:layout_above="@+id/menu_layout">
  89. <LinearLayout
  90. android:layout_width="fill_parent"
  91. android:layout_height="fill_parent"
  92. android:orientation="vertical">
  93. <TextView
  94. android:id="@+id/text"
  95. android:layout_width="wrap_content"
  96. android:layout_height="wrap_content"
  97. android:textColor="#000000"
  98. android:textSize="15px">
  99. </TextView>
  100. <ImageView
  101. android:id="@+id/pic"
  102. android:layout_width="wrap_content"
  103. android:layout_height="wrap_content">
  104. </ImageView>
  105. </LinearLayout>
  106. </ScrollView>
  107. <LinearLayout
  108. android:id="@+id/loadingLayout"
  109. android:layout_width="wrap_content"
  110. android:layout_height="wrap_content"
  111. android:orientation="vertical"
  112. android:visibility="gone"
  113. android:layout_centerInParent="true">
  114. <ProgressBar
  115. android:id="@+id/loading"
  116. android:layout_width="31px"
  117. android:layout_height="31px"
  118. android:layout_gravity="center"
  119. style="@style/progressStyle">
  120. </ProgressBar>
  121. <TextView
  122. android:layout_width="wrap_content"
  123. android:layout_height="wrap_content"
  124. android:text="正在载入"
  125. android:textSize="12px"
  126. android:textColor="#9c9c9c"
  127. android:layout_gravity="center"
  128. android:layout_below="@+id/loading">
  129. </TextView>
  130. </LinearLayout>
  131. <TableLayout
  132. android:id="@+id/menu_layout"
  133. android:layout_width="fill_parent"
  134. android:layout_height="wrap_content"
  135. android:gravity="center"
  136. android:layout_alignParentBottom="true"
  137. android:layout_marginBottom="5px">
  138. <TableRow
  139. android:layout_width="wrap_content"
  140. android:layout_height="wrap_content"
  141. android:gravity="center">
  142. <Button
  143. android:id="@+id/btn_gz"
  144. android:layout_width="wrap_content"
  145. android:layout_height="wrap_content"
  146. android:textColor="#3882b8"
  147. android:textSize="15px"
  148. android:text="关注(1231)"
  149. android:background="@drawable/lt_selector">
  150. </Button>
  151. <Button
  152. android:id="@+id/btn_pl"
  153. android:layout_width="wrap_content"
  154. android:layout_height="wrap_content"
  155. android:textColor="#3882b8"
  156. android:textSize="15px"
  157. android:text="评论(31)"
  158. android:background="@drawable/rt_selector">
  159. </Button>
  160. </TableRow>
  161. <TableRow
  162. android:layout_width="wrap_content"
  163. android:layout_height="wrap_content"
  164. android:gravity="center">
  165. <Button
  166. android:layout_width="wrap_content"
  167. android:layout_height="wrap_content"
  168. android:textColor="#3882b8"
  169. android:textSize="15px"
  170. android:layout_gravity="left"
  171. android:text="刷新"
  172. android:background="@drawable/lb_selector">
  173. </Button>
  174. <Button
  175. android:layout_width="wrap_content"
  176. android:layout_height="wrap_content"
  177. android:textColor="#3882b8"
  178. android:textSize="15px"
  179. android:text="收藏"
  180. android:background="@drawable/rb_selector">
  181. </Button>
  182. </TableRow>
  183. </TableLayout>
  184. </RelativeLayout>
  185. </LinearLayout>

上面这个布局实现起来并不复杂, 主要看看功能按钮区的4个按钮的点击上去的切换背景的效果,以关注按钮为例子看这行设置,android:background="@drawable/lt_selector",在res/drawable-mdpi目录下新建名为lt_selector.xml用来实现点击上去切换图片的效果,具体代码如下:

[java] view plain copy
  1. <?xmlversion="1.0"encoding="UTF-8"?>
  2. <selectorxmlns:android="http://schemas.android.com/apk/res/android">
  3. <itemandroid:state_focused="false"android:state_selected="false"android:state_pressed="false"android:drawable="@drawable/tbtn_1"/>
  4. <itemandroid:state_pressed="true"android:drawable="@drawable/tbtn_h_1"/>
  5. </selector>

本篇虽然看layout文件非常的长,其实仔细看看非常的简单了没有什么难和复杂的了,就是按照前面的经验控制好图片以及控件的显示位置和样式即可,本篇中用了一个ScrollView控件这个是前面没有用到过的,主要是用来当微博的内容超出显示区域的时候出现滚动条用的这个非常容易使用,所以就简单写一下到此结束了,请继续关注下一篇阅读微博的功能篇。


android开发我的新浪微博客户端-阅读微博功能篇(6.2)

6天前 上传 下载附件 (154.98 KB)

注:最近由于OAuth上传图片碰到了难题,一直在做这方面的研究导致博客很久没有更新。

  在上面一篇中已经实现了预读微博的UI界面,效果如上图,接下来完成功能部分的代码,当用户在上一个列表界面的列表中点击某一条微博的时候显示这个阅读微博的界面,在这个界面中根据传来的微博ID,然后根据这个ID通过api获取微博的具体内容进行显示。

  在ViewActivity.class的onCreate方法中添加如下代码:

[java] view plain copy
  1. privateUserInfouser;
  2. privateStringkey="";
  3. @Override
  4. publicvoidonCreate(BundlesavedInstanceState){
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.view);
  7. 。。。。。
  8. //获取上一个页面传递过来的key,key为某一条微博的id
  9. Intenti=this.getIntent();
  10. if(!i.equals(null)){
  11. Bundleb=i.getExtras();
  12. if(b!=null){
  13. if(b.containsKey("key")){
  14. key=b.getString("key");
  15. view(key);
  16. }
  17. }
  18. }
  19. }

 接下来就是view方法具体获取微博内容的方法,在这个方法中如果获取的本条微博如果包含图片那么就用前面AsyncImageLoader的方法异步载入图片并且进行显示,同时在这个方法中还要获取本条微博被转发的次数以及评论的次数,具体代码如下:

[java] view plain copy
  1. privatevoidview(Stringid){
  2. user=ConfigHelper.nowUser;
  3. OAuthauth=newOAuth();
  4. Stringurl="http://api.t.sina.com.cn/statuses/show/:id.json";
  5. Listparams=newArrayList();
  6. params.add(newBasicNameValuePair("source",auth.consumerKey));
  7. params.add(newBasicNameValuePair("id",id));
  8. HttpResponseresponse=auth.SignRequest(user.getToken(),user.getTokenSecret(),url,params);
  9. if(200==response.getStatusLine().getStatusCode()){
  10. try{
  11. InputStreamis=response.getEntity().getContent();
  12. Readerreader=newBufferedReader(newInputStreamReader(is),4000);
  13. StringBuilderbuffer=newStringBuilder((int)response.getEntity().getContentLength());
  14. try{
  15. char[]tmp=newchar[1024];
  16. intl;
  17. while((l=reader.read(tmp))!=-1){
  18. buffer.append(tmp,0,l);
  19. }
  20. }finally{
  21. reader.close();
  22. }
  23. Stringstring=buffer.toString();
  24. //Log.e("json","rs:"+string);
  25. response.getEntity().consumeContent();
  26. JSONObjectdata=newJSONObject(string);
  27. if(data!=null){
  28. JSONObjectu=data.getJSONObject("user");
  29. StringuserName=u.getString("screen_name");
  30. StringuserIcon=u.getString("profile_image_url");
  31. Log.e("userIcon",userIcon);
  32. Stringtime=data.getString("created_at");
  33. Stringtext=data.getString("text");
  34. TextViewutv=(TextView)findViewById(R.id.user_name);
  35. utv.setText(userName);
  36. TextViewttv=(TextView)findViewById(R.id.text);
  37. ttv.setText(text);
  38. ImageViewiv=(ImageView)findViewById(R.id.user_icon);
  39. AsyncImageLoaderasyncImageLoader=newAsyncImageLoader();
  40. DrawablecachedImage=asyncImageLoader.loadDrawable(userIcon,iv,newImageCallback(){
  41. @Override
  42. publicvoidimageLoaded(DrawableimageDrawable,ImageViewimageView,StringimageUrl){
  43. imageView.setImageDrawable(imageDrawable);
  44. }
  45. });
  46. if(cachedImage==null)
  47. {
  48. iv.setImageResource(R.drawable.usericon);
  49. }
  50. else
  51. {
  52. iv.setImageDrawable(cachedImage);
  53. }
  54. if(data.has("bmiddle_pic")){
  55. Stringpicurl=data.getString("bmiddle_pic");
  56. Stringpicurl2=data.getString("original_pic");
  57. ImageViewpic=(ImageView)findViewById(R.id.pic);
  58. pic.setTag(picurl2);
  59. pic.setOnClickListener(newOnClickListener(){
  60. @Override
  61. publicvoidonClick(Viewv){
  62. Objectobj=v.getTag();
  63. Intentintent=newIntent(ViewActivity.this,ImageActivity.class);
  64. Bundleb=newBundle();
  65. b.putString("url",obj.toString());
  66. intent.putExtras(b);
  67. startActivity(intent);
  68. }
  69. });
  70. DrawablecachedImage2=asyncImageLoader.loadDrawable(picurl,pic,newImageCallback(){
  71. @Override
  72. publicvoidimageLoaded(DrawableimageDrawable,ImageViewimageView,StringimageUrl){
  73. showImg(imageView,imageDrawable);
  74. }
  75. });
  76. if(cachedImage2==null)
  77. {
  78. //pic.setImageResource(R.drawable.usericon);
  79. }
  80. else
  81. {
  82. showImg(pic,cachedImage2);
  83. }
  84. }
  85. }
  86. }catch(IllegalStateExceptione){
  87. e.printStackTrace();
  88. }catch(IOExceptione){
  89. e.printStackTrace();
  90. }catch(JSONExceptione){
  91. e.printStackTrace();
  92. }
  93. }
  94. url="http://api.t.sina.com.cn/statuses/counts.json";
  95. params=newArrayList();
  96. params.add(newBasicNameValuePair("source",auth.consumerKey));
  97. params.add(newBasicNameValuePair("ids",id));
  98. response=auth.SignRequest(user.getToken(),user.getTokenSecret(),url,params);
  99. if(200==response.getStatusLine().getStatusCode()){
  100. try{
  101. InputStreamis=response.getEntity().getContent();
  102. Readerreader=newBufferedReader(newInputStreamReader(is),4000);
  103. StringBuilderbuffer=newStringBuilder((int)response.getEntity().getContentLength());
  104. try{
  105. char[]tmp=newchar[1024];
  106. intl;
  107. while((l=reader.read(tmp))!=-1){
  108. buffer.append(tmp,0,l);
  109. }
  110. }finally{
  111. reader.close();
  112. }
  113. Stringstring=buffer.toString();
  114. response.getEntity().consumeContent();
  115. JSONArraydata=newJSONArray(string);
  116. if(data!=null){
  117. if(data.length()>0){
  118. JSONObjectd=data.getJSONObject(0);
  119. Stringcomments=d.getString("comments");
  120. Stringrt=d.getString("rt");
  121. Buttonbtn_gz=(Button)findViewById(R.id.btn_gz);
  122. btn_gz.setText("转发("+rt+")");
  123. Buttonbtn_pl=(Button)findViewById(R.id.btn_pl);
  124. btn_pl.setText("评论("+comments+")");
  125. }
  126. }
  127. }
  128. catch(IllegalStateExceptione){
  129. e.printStackTrace();
  130. }catch(IOExceptione){
  131. e.printStackTrace();
  132. }catch(JSONExceptione){
  133. e.printStackTrace();
  134. }
  135. }
  136. }

在上面的方法中对于微博中包含的图片显示尺寸进行了特别的处理,如果直接把获取的图片显示在ImageView中,因为当图片宽高超过手机屏幕的时候,系统会自动按照手机的屏幕按比例缩放图片进行显示,但是我发现一个现象图片的高虽然是按照比例缩小了,但是图片占据的高仍旧是原来图片的高度照成真实图片和文字内容之间多了很高的一块空白,这个现象非常的奇怪,所以我写了如下方法进行处理:

[java] view plain copy
  1. privatevoidshowImg(ImageViewview,Drawableimg){
  2. intw=img.getIntrinsicWidth();
  3. inth=img.getIntrinsicHeight();
  4. Log.e("w",w+"/"+h);
  5. if(w>300)
  6. {
  7. inthh=300*h/w;
  8. Log.e("hh",hh+"");
  9. LayoutParamspara=view.getLayoutParams();
  10. para.width=300;
  11. para.height=hh;
  12. view.setLayoutParams(para);
  13. }
  14. view.setImageDrawable(img);
  15. }

本篇到这里就结束了,请继续关注下一篇。

关于微博服务端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添加如下代码:

[java] view plain copy
  1. publicoverridevoidInit()
  2. {
  3. IServiceLocatorinjector=
  4. newWindsorServiceLocator(
  5. newWindsorContainer(
  6. newXmlInterpreter(
  7. newConfigResource("oauth.net.components"))));
  8. ServiceLocator.SetLocatorProvider(()=>injector);
  9. }

接下来是比较重要,就是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,这个提供给客户端测试用。 具体代码如下:

[java] view plain copy
  1. publicsealedclassConsumerStore:InMemoryConsumerStore,IConsumerStore
  2. {
  3. internalstaticreadonlyIConsumerFixedConsumer=newOAuthConsumer("2433927322","87f042c9e8183cbde0f005a00db1529f","测试应用",ConsumerStatus.Valid);
  4. publicConsumerStore()
  5. {
  6. this.ConsumerDictionary.Add(
  7. ConsumerStore.FixedConsumer.Key,
  8. ConsumerStore.FixedConsumer);
  9. }
  10. publicoverrideboolAdd(IConsumerconsumer)
  11. {
  12. thrownewNotSupportedException("Consumerscannotbeaddedtothisstore--itisfixed.");
  13. }
  14. publicoverrideboolContains(stringconsumerKey)
  15. {
  16. returnConsumerStore.FixedConsumer.Key.Equals(consumerKey);
  17. }
  18. publicoverrideboolUpdate(IConsumerconsumer)
  19. {
  20. thrownewNotSupportedException("Consumerscannotbeupdatedinthisstore--itisfixed.");
  21. }
  22. publicoverrideboolRemove(IConsumerconsumer)
  23. {
  24. thrownewNotSupportedException("Consumerscannotberemovedfromthisstore--itisfixed.");
  25. }
  26. }

四、接下来就是request_token功能,新建RequestTokenHandler.cs ,这个是OAuth.Net.ServiceProvider.RequestTokenHandler子类,并且是httpHandlers所以需要在Web.config中添加httpHandlers配置,这个用来接收客户端程序的请求,返回给客户端程序Request Token和Request Secret用,具体代码如下:

[java] view plain copy
  1. publicsealedclassRequestTokenHandler:OAuth.Net.ServiceProvider.RequestTokenHandler
  2. {
  3. protectedoverridevoidIssueRequestToken(HttpContexthttpContext,OAuthRequestContextrequestContext)
  4. {
  5. //产生RequestToken
  6. IRequestTokentoken=this.GenerateRequestToken(httpContext,requestContext);
  7. requestContext.RequestToken=token;
  8. UricallbackUri;
  9. if(Uri.TryCreate(requestContext.Parameters.Callback,UriKind.Absolute,outcallbackUri))
  10. {
  11. if(!ServiceProviderContext.CallbackStore.ContainsCallback(token))
  12. {
  13. //保存Callback地址了
  14. ServiceProviderContext.CallbackStore.AddCallback(token,callbackUri);
  15. }
  16. }
  17. else
  18. OAuthRequestException.ThrowParametersRejected(newstring[]{Constants.CallbackParameter},"NotavalidUri.");
  19. //把token.Token和token.Secret输出到客户端,
  20. requestContext.ResponseParameters[Constants.TokenParameter]=token.Token;
  21. requestContext.ResponseParameters[Constants.TokenSecretParameter]=token.Secret;
  22. }
  23. protectedoverrideIRequestTokenGenerateRequestToken(HttpContexthttpContext,OAuthRequestContextrequestContext)
  24. {
  25. returnServiceProviderContext.TokenGenerator.CreateRequestToken(requestContext.Consumer,requestContext.Parameters);
  26. }
  27. }

五、 接着是authorize功能,新建名为authorize.aspx的页面,用来给用户输入账号和密码进行授权的页面,这个页面很简单具体如下图,在这个页面中获取用户输入的账户和密码跟数据库中存储的用户账号和密码进行验证,如果验证通过返回之前客户端提供的callback地址,并且给这个地址添加一个校验码,具体代码如下:

[java] view plain copy
  1. publicpartialclassauthorize:System.Web.UI.Page
  2. {
  3. protectedvoidPage_Load(objectsender,EventArgse)
  4. {
  5. }
  6. protectedvoidButton1_Click(objectsender,EventArgse)
  7. {
  8. if(loginName.Text=="test"&&password.Text=="123")
  9. {
  10. stringtoke=Request.Params["oauth_token"];
  11. IRequestTokentk=ServiceProviderContext.TokenStore.GetRequestToken(toke);
  12. Uricallback=ServiceProviderContext.CallbackStore.GetCalback(tk);
  13. stringoauth_verifier=ServiceProviderContext.VerificationProvider.Generate(tk);
  14. Response.Redirect(callback.ToString()+"?oauth_verifier="+oauth_verifier);
  15. }
  16. }
  17. }

六、接下来就是access_token功能,新建AccessTokenHandler.cs , 这个是OAuth.Net.ServiceProvider.AccessTokenHandler子类,并且是httpHandlers所以需要在Web.config中添加httpHandlers配置,这个用来接收客户端程序的请求,返回给客户端程序Access Token和Access Secret用,具体代码如下:

[java] view plain copy
  1. publicsealedclassAccessTokenHandler:OAuth.Net.ServiceProvider.AccessTokenHandler
  2. {
  3. protectedoverridevoidIssueAccessToken(HttpContexthttpContext,OAuthRequestContextrequestContext)
  4. {
  5. //产生accesstoken
  6. IAccessTokenaccessToken=this.GenerateAccessToken(httpContext,requestContext);
  7. accessToken.Status=TokenStatus.Authorized;
  8. //把accessToken和accessSecret输出到客户端,
  9. requestContext.ResponseParameters[Constants.TokenParameter]=accessToken.Token;
  10. requestContext.ResponseParameters[Constants.TokenSecretParameter]=accessToken.Secret;
  11. }
  12. protectedoverrideIAccessTokenGenerateAccessToken(HttpContexthttpContext,OAuthRequestContextrequestContext)
  13. {
  14. returnServiceProviderContext.TokenGenerator.CreateAccessToken(requestContext.Consumer,requestContext.RequestToken);
  15. }
  16. }
  17. publicclassTokenGenerator:ITokenGenerator
  18. {
  19. internalstaticreadonlyIRequestTokenFixedRequestToken=newOAuthRequestToken("requestkey",
  20. "requestsecret",
  21. ConsumerStore.FixedConsumer,
  22. TokenStatus.Authorized,
  23. null,
  24. ServiceProviderContext.DummyIdentity,
  25. newstring[]{});
  26. internalstaticreadonlyIAccessTokenFixedAccessToken=newOAuthAccessToken(
  27. "accesskey",
  28. "accesssecret",
  29. ConsumerStore.FixedConsumer,
  30. TokenStatus.Authorized,
  31. TokenGenerator.FixedRequestToken);
  32. publicIRequestTokenCreateRequestToken(IConsumerconsumer,OAuthParametersparameters)
  33. {
  34. returnTokenGenerator.FixedRequestToken;
  35. }
  36. publicIAccessTokenCreateAccessToken(IConsumerconsumer,IRequestTokenrequestToken)
  37. {
  38. returnTokenGenerator.FixedAccessToken;
  39. }
  40. }
  41. publicclassTokenStore:InMemoryTokenStore,ITokenStore
  42. {
  43. publicTokenStore()
  44. {
  45. this.RequestTokenDictionary.Add(
  46. TokenGenerator.FixedRequestToken.Token,
  47. TokenGenerator.FixedRequestToken);
  48. this.AccessTokenDictionary.Add(
  49. TokenGenerator.FixedAccessToken.Token,
  50. TokenGenerator.FixedAccessToken);
  51. }
  52. publicoverrideboolAdd(IRequestTokentoken)
  53. {
  54. thrownewNotSupportedException("Tokenscannotbeaddedtothetokenstore--itisfixed.");
  55. }
  56. publicoverrideboolAdd(IAccessTokentoken)
  57. {
  58. thrownewNotSupportedException("Tokenscannotbeaddedtothetokenstore--itisfixed.");
  59. }
  60. publicoverrideboolUpdate(IRequestTokentoken)
  61. {
  62. thrownewNotSupportedException("Tokenscannotbeupdatedinthetokenstore--itisfixed.");
  63. }
  64. publicoverrideboolUpdate(IAccessTokentoken)
  65. {
  66. thrownewNotSupportedException("Tokenscannotbeupdatedinthetokenstore--itisfixed.");
  67. }
  68. publicoverrideboolRemove(IRequestTokentoken)
  69. {
  70. thrownewNotSupportedException("Tokenscannotberemovedfromthetokenstore--itisfixed.");
  71. }
  72. publicoverrideboolRemove(IAccessTokentoken)
  73. {
  74. thrownewNotSupportedException("Tokenscannotberemovedfromthetokenstore--itisfixed.");
  75. }
  76. }

这样就完成了一个最最简单小型的服务端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)

在阅读微博的功能篇中,如果微博包含了图片就会在微博正文下面显示该张图片,但是这个图片只是张缩略图,这样就需要提供一个能放大缩小查看这张图片的功能,当点击正文中的缩略图的时候显示一个简单的图片浏览器功能,提供图片的放大、缩小、拖拽操作方便用户查看图片,同时也提供保存图片到手机的功能。本功能的UI比较简单就不单独分篇讲了,具体的实现效果如上图。

新建ImageActivity.java作为图片浏览Activity,在res/layout下新建image.xml的Layout作为图片浏览的布局文件,image.xml布局代码很简单了就不详细解释了直接贴代码:


[java] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="wrap_content"
  5. android:layout_height="wrap_content"
  6. android:orientation="vertical">
  7. <LinearLayout
  8. android:layout_width="fill_parent"
  9. android:layout_height="41px"
  10. android:background="@drawable/imagebar_bg">
  11. <RelativeLayout
  12. android:layout_width="fill_parent"
  13. android:layout_height="fill_parent"
  14. android:layout_weight="2">
  15. <Button
  16. android:id="@+id/returnBtn"
  17. android:layout_width="63px"
  18. android:layout_height="29px"
  19. android:layout_centerInParent="true"
  20. android:text="返回"
  21. android:textColor="#ffffff"
  22. android:background="@drawable/btn1_bg">
  23. </Button>
  24. </RelativeLayout>
  25. <RelativeLayout
  26. android:layout_width="fill_parent"
  27. android:layout_height="fill_parent"
  28. android:layout_weight="1">
  29. <TextView
  30. android:layout_width="wrap_content"
  31. android:layout_height="wrap_content"
  32. android:layout_centerInParent="true"
  33. android:text="浏览图片"
  34. android:textColor="#ffffff">
  35. </TextView>
  36. </RelativeLayout>
  37. <RelativeLayout
  38. android:layout_width="fill_parent"
  39. android:layout_height="fill_parent"
  40. android:layout_weight="2">
  41. <Button
  42. android:id="@+id/downBtn"
  43. android:layout_width="60px"
  44. android:layout_height="29px"
  45. android:layout_centerInParent="true"
  46. android:text="下载"
  47. android:textColor="#ffffff"
  48. android:background="@drawable/btn2_bg">
  49. </Button>
  50. </RelativeLayout>
  51. </LinearLayout>
  52. <RelativeLayout
  53. android:layout_width="fill_parent"
  54. android:layout_height="fill_parent">
  55. <MySinaWeiBo.ui.ImageZoomView
  56. xmlns:android="http://schemas.android.com/apk/res/android"
  57. android:id="@+id/pic"
  58. android:layout_width="fill_parent"
  59. android:layout_height="fill_parent">
  60. </MySinaWeiBo.ui.ImageZoomView>
  61. <ZoomControls
  62. android:id="@+id/zoomCtrl"
  63. android:layout_width="wrap_content"
  64. android:layout_height="wrap_content"
  65. android:layout_alignParentRight="true"
  66. android:layout_alignParentBottom="true">
  67. </ZoomControls>
  68. </RelativeLayout>
  69. </LinearLayout>

上面的代码中用到了一个自定义控件MySinaWeiBo.ui.ImageZoomView,这个就是整个功能的核心部分,用来实现图片的放大、缩小、拖拽的一个图片显示控件,这个控件非我原创,是参考了Android one finger zoom tutorial 这篇博客写出来的,所以我在这里也不贴实现代码了,有兴趣的大家可以直接看看这个文章。 接下要做的就是用这个ImageZoomView来显示图片,在阅读微博内容的页面中当点击内容中的缩略图片的时候会把这个缩略图对应的原图的url传给当前的这个ImageActivity,那么在ImageActivity的onCreate方法中根据这个url获取图片并且设置给ImageZoomView。在onCreate方法中代码如下:

[java] view plain copy
  1. @Override
  2. publicvoidonCreate(BundlesavedInstanceState){
  3. 。。。。。
  4. Intenti=this.getIntent();
  5. if(i!=null){
  6. Bundleb=i.getExtras();
  7. if(b!=null){
  8. if(b.containsKey("url")){
  9. Stringurl=b.getString("url");
  10. mZoomView=(ImageZoomView)findViewById(R.id.pic);
  11. Drawableimg=AsyncImageLoader.loadImageFromUrl(url);
  12. image=drawableToBitmap(img);
  13. mZoomView.setImage(image);
  14. mZoomState=newZoomState();
  15. mZoomView.setZoomState(mZoomState);
  16. mZoomListener=newSimpleZoomListener();
  17. mZoomListener.setZoomState(mZoomState);
  18. mZoomView.setOnTouchListener(mZoomListener);
  19. resetZoomState();
  20. }
  21. }
  22. }
  23. 。。。。。。
  24. }
[java] view plain copy
  1. privatevoidresetZoomState(){
  2. mZoomState.setPanX(0.5f);
  3. mZoomState.setPanY(0.5f);
  4. finalintmWidth=image.getWidth();
  5. finalintvWidth=mZoomView.getWidth();
  6. Log.e("iw:",vWidth+"");
  7. mZoomState.setZoom(1f);
  8. mZoomState.notifyObservers();
  9. }
  10. publicBitmapdrawableToBitmap(Drawabledrawable){
  11. Bitmapbitmap=((BitmapDrawable)drawable).getBitmap();
  12. returnbitmap;
  13. }
[java] view plain copy
  1. ZoomControlszoomCtrl=(ZoomControls)findViewById(R.id.zoomCtrl);
  2. zoomCtrl.setOnZoomInClickListener(newOnClickListener(){
  3. @Override
  4. publicvoidonClick(Viewview){
  5. floatz=mZoomState.getZoom()+0.25f;
  6. mZoomState.setZoom(z);
  7. mZoomState.notifyObservers();
  8. }
  9. });
  10. zoomCtrl.setOnZoomOutClickListener(newOnClickListener(){
  11. @Override
  12. publicvoidonClick(Viewv){
  13. floatz=mZoomState.getZoom()-0.25f;
  14. mZoomState.setZoom(z);
  15. mZoomState.notifyObservers();
  16. }
  17. });

这样一个简单的图片浏览器功能就完成了,支持放大缩小并且还能拖拽,基本上达到应用需求。

更多相关文章

  1. Android多功能课程表项目实现(完整版)
  2. Android官方入门文档[13]暂停和恢复一个Activity活动
  3. Android应用程序未捕获异常并提示用户发送给开发者
  4. Android用户界面UI组件--AdapterView及其子类(三) ExpandableLis
  5. Android(安卓)Material Design之TextInputLayout
  6. Android这可能是当今最火,功能最全的快速开发框架了
  7. Android记事本NotePad应用功能拓展(三)
  8. Android之用户定位(三)
  9. Android比较好的10个开源框架

随机推荐

  1. sql获取分组排序后数据的脚本
  2. 使用mongovue把sqlserver数据导入mongodb
  3. sql server自动生成拼音首字母的函数
  4. sqlserver实现oracle的sequence方法
  5. sql中count或sum为条件的查询示例(sql查
  6. mssql使用存储过程破解sa密码
  7. 没有sa密码无法集成windows身份验证登录
  8. 参考sql2012存储过程写的统计所有用户表
  9. sql server 2000阻塞和死锁问题的查看与
  10. sql 2000 无法执行查询,因为一些文件缺少