- 浏览: 21369102 次
- 性别:
- 来自: 杭州
最新评论
-
ZY199266:
配置文件还需要额外的配置ma
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
ZY199266:
我的一访问为什么是 /mavenwebdemo/WEB-I ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
lvgaga:
我又一个问题就是 如果像你的这种形式写。配置文件还需要额外的 ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
lvgaga:
我的一访问为什么是 /mavenwebdemo/WEB-I ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
y1210251848:
你的那个错误应该是项目所使用的目标框架不支持吧
log4net配置(web中使用log4net,把web.config放在单独的文件中)
android开发新浪微博客户端 完整攻略 [新手必读][转]
最后还要在说一下,很抱歉上面内容基本上属于废话没有什么实质内容了但是既然是第一篇还是得象征性的交代一下,从下篇开始讲具体的内容。
一、接下来的任务就是在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部分的实现,测试运行模拟器中查看效果,基本上跟最上面的设计效果图相符,测试效果图如下:
首先说明一下新浪微博提供了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){
-
......
-
}
-
}
-
关于载入页面的sqlite就说到这里了,下一篇说说OAuth认证实现。
android开发我的新浪微博客户端-OAuth篇(2.1) 本篇说说关于OAuth授权认证的事情,新浪开放api都必须在这个基础上才能调用,所以有必要专门来讲讲,前面的文章中已经提到过关于新浪微博提供了OAuth和Base OAuth两种认证方式,并且本项目采用OAuth认证方式,至于为什么采用这个OAuth认证而不采用Base OAuth认证原因很简单,自从Twitter只支持OAuth认证方式以来,各大应用都纷纷转向OAuth认证方式,而新浪微博的开放平台也将在近日停止Base OAuth的认证方式。
OAuth的基本概念,OAUTH协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是OAUTH的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此OAUTH是安全的。同样新浪微博提供OAuth认证也是为了保证用户账号和密码的安全,在这里通过OAuth建立普通新浪微博用户、客户端程序(我们正在开发的这个android客户端程序)、新浪微博三者之间的相互信任关系,让客户端程序(我们正在开发的这个android客户端程序)不需要知道用户的账号和密码也能浏览、发布微博,这样有效的保护了用户账号的安全性不需要把账号密码透露给客户端程序又达到了通过客户端程序写微博看微博目的。这个是OAuth的作用。
这样就完成了OAuth功能类的开发,后面都会用到这个类相关的方法。本篇到这里就算是完结请继续关注后面的文章。 |
android开发我的新浪微博客户端-用户授权页面UI篇(3.1)
上一篇讲了讲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)。
接下来是本文的重点部分,半透明弹窗用Dialog控件进行实现,首先为这个半透明弹窗新建一个名为dialog.xml的Layout,这个Layout主要是对4个元素进行布局,如图所示分别为i小图标、信息提示、中间文字、开始按钮,首先用LinearLayout对i小图标和信息提示进行水平布局,中间文字以一个TextView跟在下面,对于开始按钮是用RelativeLayout进行底部对齐显示。具体代码如下:
完成了半透明弹窗的Layout定义接下来我们要做的就是为它写一个自定义样式来实现我们想要的显示效果,首先我们需准备一个圆角的半透明png图片名为dia_bg.png并且添加到drawable中,接下来再res/values文件夹新建名为 dialogStyle.xml的resources样式文件,具体代码如下:
这个样式文件的说明如下
最后运行查看效果,到这里我们的任务已经完成了。请关注下一篇功能篇。 |
android开发我的新浪微博客户端-用户授权页面功能篇(3.2) 在上一篇实现了用户授权页面的UI,如上图,接下来要做的就是在这个基础上完成功能部分真正实现用户的授权认证,这一篇是android开发我的新浪微博客户端-OAuth篇(2.1)的具体应用篇原理就不多解释了不懂的看OAuth篇即可。认证过程从点击开始按钮然后跳转到新浪的授权页面,接着用户在新浪的页面里输入自己的账户和密码确定后返回用户授权页面。首先给开始按钮添加点击事件代码,代码中主要是调用我们前面android开发我的新浪微博客户端-OAuth篇(2.1)完成的OAuth类的RequestAccessToken方法用来获取oauth_verifier,具体代码如下:
|
首先回顾一下功能流程当用户开启软件显示载入页面时程序首先去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>
底部菜单实现,原本我是采用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>
-
<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>
-
</style>
- </resources>
-
<?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>
-
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();
-
-
......
-
}
-
- });
android开发我的新浪微博客户端-登录页面功能篇(4.2) 上一篇中完成了如上图的UI部分的实现,现在继续来讲功能的实现,用户登录操作主要就是账号列表显示和选择账号登录两个功能其他的都是些简单的辅助功能,首先是点击id为iconSelectBtn的ImageButton时显示用户选择窗口,这个时候去数据库中获取账号记录然后在选择窗口中以列表方式显示出来,通过上一篇已经知道Id为list的ListView控件来显示账号列表,首先是从数据库中获取所有的账户记录然后设置默认选中的用户账号代码如下:
接下类首先为Id为list的ListView控件准备数据Adapter,这个Adapter非常简单就是普通的adapter继承BaseAdapter即可,代码如下:
最后添加如下方法,用来当这个登录LoginActivity结束的时候保存当前选择的账户名称到SharedPreferences中,以便帮用户记住登录账号的功能,就是前面的initUser() 初始账号的方法中会获取保存在SharedPreferences中的账户名称,代码如下:
|
android开发我的新浪微博客户端-用户首页面UI篇(5.1)
在前篇完成了用户登录功能后开始用户首页的开发,用户的首页主要的内容是当前登录用户关注的微博列表,本篇先来讲讲UI的实现,效果如上图,整个页面分为上、中、下三部分,上面部分是工具条,显示当前登录用户的昵称以及写微博、刷新两个功能按钮;中间部分是当前用户关注的最新微博列表,下面部分是功能切换栏,用来进行各个功能之间的切换。 首先新建名为HomeActivity.java的Activity作为用户首页,然后在res/layout目录下新建名为home.xml的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,内容如下:
本篇到这里就结束了,下一篇继续讲用户首页的功能实现,请关注 |
上一篇完成用户首页的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;
-
}
-
- }
-
<?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,代码如下:
关于微博服务端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添加如下代码:
接下来是比较重要,就是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,这个提供给客户端测试用。 具体代码如下:
这样就完成了一个最最简单小型的服务端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"); ...... }
转自:http://www.eoeandroid.com/thread-67298-1-1.html |
相关推荐
android开发新浪微博客户端+完整攻略+源码 android开发新浪微博客户端+完整攻略+源码 android开发新浪微博客户端+完整攻略+源码 android开发新浪微博客户端+完整攻略+源码
Android开发入门之路(初学者必读).zip
最全的ECSHOP模板开发教程完全攻略,适合新手开发项目。
Android手机程序安装指南(新手必读)在Android平台上安装文件的后缀名一般为“.apk”,和WM微软平台上的安装文件的后缀名“.exe”或者S60塞班平台上的安装文件的后缀名“.sis或.sisx”类似。所以只要看到“.apk”...
Android开发入门必读 阅读顺序: Android应用开发详解.pdf Android应用框架原理与程序设计36技第三版.pdf
黄隽实编著的《Android和PHP开发*实践(第2版移动开发)》以一个完整的微博应用项目实例为主线,由浅入深地讲解了Android客户端开发和PHP服务端开发的思路和技巧。从前期的产品设计、架构设计,到客户端和服务端的编码...
"WinAVR新手必读.pdf",应该来说,是每一个想入门GCC必读的内容,学习以后,只要几分钟就能软件入门了.
android 高危权限对照表开发必读android 高危权限对照表开发必读android 高危权限对android 高危权限对照表开发必读照表开发必读
[入门必读]android移动开发 十个 案例 值得学习打包
新手必读之CSS教程新手必读之CSS教程新手必读之CSS教程新手必读之CSS教程新手必读之CSS教程新手必读之CSS教程
Android Dev Guide中文版,android入门必读教程!
android高级工程师的进阶之路,深入原理,不同于市面上的多如牛毛的android开发书籍,这本书更有针对性,对于面试求职,是一本必读的书籍
PCB设计流程(新手必读) PCB设计流程(新手必读)
java新手必读java学习的捷径,java新手必读java学习的捷径
装修新手必读:一站式新房装修全攻略.pptx
android开发必备的基础资料,,帮你完成android初期学习所有的配置信息!!!!
JavaSE基础教程(新手必读) JavaSE基础教程(新手必读)
西门子x65手机新手必读教程。转自0110手机论坛
Java编程新手必读-- 通过本资料希望可以帮助你尽快掌握JAVA
如果你是EXTJS新手,如果你想要了解EXTJS,那么你还在等什么,下载看看吧,对你学习EXT有很大的帮助哦。。。