如何从webview 中获取表单数据

1. 先分析html 表单

1
2
3
4
5
6
7
8
9
10
<form method="POST" id="login-form" class="adjacent" action="/accounts/login/?force_classic_login=&next=/oauth/authorize/%3Fclient_id%3Dcf2cd88a0b7745a4b4f8ac673190f659%26redirect_uri%3Dhttp%253A%252F%252Finstafollow.elasticbeanstalk.com%252Fredirect.php%253FclientKey%253Dcf2cd88%26response_type%3Dcode">
<input type="hidden" name="csrfmiddlewaretoken" value="fd942039c72570e71786a8a5a13566b5"/>
<p><label for="id_username">��ㄦ�峰��:</label> <input name="username" maxlength="30" autocapitalize="off" autocorrect="off" type="text" id="id_username" /></p>
<p><label for="id_password">瀵����:</label> <input autocapitalize="off" autocorrect="off" type="password" name="password" id="id_password" /></p>
<p class="form-actions">
<a href="/accounts/password/reset/">蹇�璁板�����锛�</a>
<input type="submit" class="button-green" value="��诲��" />
</p>
</form>

写js代码

1
2
3
4
5
6
String javascript = "javascript: document.getElementsByClassName(\"button-green\")[0].onclick = function() {\n" +
" var username = document.getElementById(\"id_username\").value;\n" +
" var password = document.getElementById(\"id_password\").value;\n" +
" bridge.saveData(username, password);\n" +
" };";

2. js调用的java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MyBridge {
private static String mUsername;
private static String mPassword;
Context mContext;
public MyBridge(Context context) {
mContext = context;
}
@JavascriptInterface
public void saveData(String username, String password) {
Toast.makeText(mContext, username + " " + password, Toast.LENGTH_SHORT).show();
mUsername = username;
mPassword = password;
}
public static String getUsername() {
return mUsername;
}
public static String getPassword() {
return mPassword;
}
}

3. 完整的webview布局及实现

布局

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
public class OAuthActivity extends Activity {
private static final String OAUTH_URI = "https://instagram.com/oauth/authorize/";
public static final String INTENT_KEY_USERNAME = "username";
public static final String INTENT_KEY_PASSWORD = "password";
public static final String INTENT_KEY_REGISTER_DATA = "registerData";
private RegisterData mRegisterData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_oauth);
init();
}
@SuppressLint("SetJavaScriptEnabled")
private void init() {
mRegisterData = (RegisterData) getIntent().getSerializableExtra(INTENT_KEY_REGISTER_DATA);
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
myWebView.addJavascriptInterface(new MyBridge(OAuthActivity.this), "bridge");
myWebView.setWebViewClient(new MyWebViewClient());
myWebView.loadUrl(getOAuthUrl());
}
private String getOAuthUrl() {
// https://instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=token
StringBuilder builder = new StringBuilder();
builder.append(OAUTH_URI);
builder.append("?client_id=" + mRegisterData.getClientId());
builder.append("&redirect_uri=" + Uri.encode(mRegisterData.getRedirectUri()));
builder.append("&response_type=" + mRegisterData.getReponseCode());
String url = builder.toString();
System.out.println("====== getOAuthUrl ====" + url);
return url;
}
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
System.out.println("====== shouldOverrideUrlLoading url ====" + url);
if (Uri.parse(url).getHost().equals(Uri.parse(mRegisterData.getRedirectUri()).getHost())) {
String error = Uri.parse(url).getQueryParameter("error");
handleReturnCode(error);
CookieSyncManager.createInstance(OAuthActivity.this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
return true;
} else {
return false;
}
}
@Override
public void onPageFinished(WebView view, String url) {
//System.out.println("====== onPageFinished url====" + url);
if (url.contains("login")) {
view.addJavascriptInterface(new MyBridge(OAuthActivity.this), "bridge");
String javascript = "javascript: document.getElementsByClassName(\"button-green\")[0].onclick = function() {\n" +
" var username = document.getElementById(\"id_username\").value;\n" +
" var password = document.getElementById(\"id_password\").value;\n" +
" bridge.saveData(username, password);\n" +
" };";
view.loadUrl(javascript);
}
super.onPageFinished(view, url);
}
}
private void handleReturnCode(String error) {
System.out.println("====== handleReturnCode error ====" + error);
Intent intent = new Intent();
if (error == null) {
intent.putExtra(INTENT_KEY_USERNAME, MyBridge.getUsername());
intent.putExtra(INTENT_KEY_PASSWORD, MyBridge.getPassword());
setResult(RESULT_OK, intent);
} else {
setResult(RESULT_CANCELED, intent);
}
finish();
}
}

4. 调用OAuth 的Activtiy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MainActivity extends ActionBarActivity {
private static final String CLIENT_ID = "cf2cd88a0b7745a4b4f8ac673190f659";
private static final String REDIRECT_URI = "http://instafollow.elasticbeanstalk.com/redirect.php?clientKey=cf2cd88";
private static final String RESPONSE_TYPE = "code";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onClickLogin(View v) {
Intent intent = new Intent(this, OAuthActivity.class);
RegisterData registerData = new RegisterData();
registerData.setClientId(CLIENT_ID);
registerData.setRedirectUri(REDIRECT_URI);
registerData.setReponseCode(RESPONSE_TYPE);
intent.putExtra(OAuthActivity.INTENT_KEY_REGISTER_DATA,registerData);
startActivityForResult(intent, 200);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != 200) return;
if (resultCode == RESULT_OK) {
Toast.makeText(this, "success " + data.getStringExtra(OAuthActivity.INTENT_KEY_USERNAME)
+ data.getStringExtra(OAuthActivity.INTENT_KEY_PASSWORD), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "fail ", Toast.LENGTH_SHORT).show();
}
}
}

TinderLiker 开发总结

概览:

1. 采用ORM数据库LitePal

2. ToolBar的使用

3. 最新兼容库的下拉刷新

4. 工具类


1. 采用ORM数据库LitePal

(1)官方介绍

LitePal is an Open Source Android library that allows developers to use SQLite database extremely easy. You can finish most of the database operations without writing even a SQL statement, including create or upgrade tables, crud operations, aggregate functions, etc. The setup of LitePal is quite simple as well, you can integrate it into your project in less than 5 minutes.

官方地址GitHub

系列博客地址(中文的)

(2)欣赏一下增删改查(配置超级简单,见官网)

1
2
3
4
Album album = new Album();
album.setName("album");
album.setPrice(10.99f);
album.save();

1
2
3
DataSupport.delete(Song.class, id);
DataSupport.deleteAll(Song.class, "duration > ?" , "350");

1
2
3
4
5
6
7
Album albumToUpdate = new Album();
albumToUpdate.setPrice(20.99f); // raise the price
albumToUpdate.update(id);
Album albumToUpdate = new Album();
albumToUpdate.setPrice(20.99f); // raise the price
albumToUpdate.updateAll("name = ?", "album");

1
2
3
4
5
Song song = DataSupport.find(Song.class, id);
List<Song> allSongs = DataSupport.findAll(Song.class);
List<Song> songs = DataSupport.where("name like ?", "song%").order("duration").find(Song.class);

(3)踩过的坑

a. 定义model时,加上id,方便以后更新数据

如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Address extends DataSupport {
private long id;
private String name;
private float lat;
private float lng;
private Date date;
private boolean checked;
private boolean my_location;
private String owner_id;
public Address() {
}
...
}

更新时就可以

1
2
3
4
Tinder savedTinder = savedTinders.get(0);
savedTinder.setChanged_date(t.getChanged_date());
savedTinder.setRelation(t.getRelation());
savedTinder.update(savedTinder.getId());
b. 更新到默认值的方法有点特别

源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Updates the corresponding record by id. Use setXxx to decide which
* columns to update.
*
* <pre>
* Person person = new Person();
* person.setName("Jim");
* person.update(1);
* </pre>
*
* This means that the name of record 1 will be updated into Jim.<br>
*
* <b>Note: </b> 1. If you set a default value to a field, the corresponding
* column won't be updated. Use {@link #setToDefault(String)} to update
* columns into default value. 2. This method couldn't update foreign key in
* database. So do not use setXxx to set associations between models.
*
* @param id
* Which record to update.
* @return The number of rows affected.
*/
public synchronized int update(long id) {
try {
UpdateHandler updateHandler = new UpdateHandler(Connector.getDatabase());
int rowsAffected = updateHandler.onUpdate(this, id);
getFieldsToSetToDefault().clear();
return rowsAffected;
} catch (Exception e) {
throw new DataSupportException(e.getMessage());
}
}

所以更新到默认值的时候需要这么做:

1
2
3
4
5
6
7
8
9
long id = address.getId();
if (id > 0) {
if (!address.isChecked()) {
address.setToDefault("checked");
}
address.update(id);
} else {
address.save();
}
c. 别忘了Proguard

-keep class org.litepal.* { ; }

2. ToolBar的使用

(1)依赖

1
2
3
4
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.2'
}

(2)Theme配制

style.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="AppTheme.Base">
<!-- Customize your theme here. -->
</style>
<style name="AppTheme.Base" parent="Theme.AppCompat">
<item name="windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<!-- Actionbar color -->
<item name="colorPrimary">@color/red</item>
<!--Status bar color-->
<item name="colorPrimaryDark">@color/red</item>
<!--Window color-->
<item name="android:windowBackground">@android:color/white</item>
</style>
</resources>

v21/style.xml

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="AppTheme.Base">
<!--Navigation bar color-->
<item name="android:navigationBarColor">@color/red</item>
</style>
</resources>

(3)单独定义toolBar的布局文件toolbar.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize">
</android.support.v7.widget.Toolbar>

(4)其他布局文件中引入toolBar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
layout="@layout/toolbar"
android:id="@+id/toolbar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
...
</ScrollView>
</LinearLayout>

(5)让toolBar支持actionBar

1
2
3
4
5
6
7
8
9
10
11
12
13
private void initToolBar() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setTitle(mTinder.getName());
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}

3. 最新兼容库的下拉刷新

(1)依赖

1
2
3
4
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.2'
}

(2)布局和其他的下拉刷新一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/frag_liker_refresh_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/frag_liker_bottom_layout">
<GridView
android:id="@+id/frag_liker_gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
android:gravity="fill"
android:horizontalSpacing="8dp"
android:numColumns="3"
android:stretchMode="columnWidth"
android:verticalSpacing="8dp" />
</android.support.v4.widget.SwipeRefreshLayout>
<TextView
android:id="@+id/empty_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginTop="150dp"
android:layout_centerHorizontal="true"
android:textColor="#ababab"
android:visibility="gone"
android:gravity="center"
android:lineSpacingExtra="6dp"
android:drawableTop="@drawable/ic_mainlikepass_smile"
android:text="@string/frag_tinder_liker_empty" />
</RelativeLayout>

注意这里展示空列表的方式

(3)刚进入页面显示刷新图标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 定义Runnable
private Runnable mProgressRunnable = new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(true);
}
};
// 第一次显示刷新
mHandler.postDelayed(mProgressRunnable, 100);
// 停止刷新
mHandler.removeCallbacks(mProgressRunnable);
mSwipeRefreshLayout.setRefreshing(false);

如果只是在初始化是设置mSwipeRefreshLayout.setRefreshing(true);,刷新图标不会显示。

4. 工具类

(1)把形如1988-08-17T00:00:00.000Z的时间转换为毫秒数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static int getAge(String formattedBirthday) {
// birth_date : 1988-08-17T00:00:00.000Z
String format = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.US);
int age = 0;
Calendar cal = Calendar.getInstance();
int nowYear = cal.get(Calendar.YEAR);
try {
Date date = sdf.parse(formattedBirthday);
cal.setTime(date);
int birthYear = cal.get(Calendar.YEAR);
age = nowYear - birthYear;
if (age < 0) age = 0;
} catch (ParseException e) {
e.printStackTrace();
}
return age;
}

(2)利用Android的工具方法得到相对时间,如一天前,一周前等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static CharSequence getActiveTime(String formattedTime) {
String format = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.US);
CharSequence activeTime = "";
Calendar cal = Calendar.getInstance();
try {
Date date = sdf.parse(formattedTime);
cal.setTime(date);
activeTime = DateUtils.getRelativeTimeSpanString(cal.getTimeInMillis());
} catch (ParseException e) {
e.printStackTrace();
}
return activeTime;
}

toolbar 和 drawer 搭配

1.先看看效果

2.定义style.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="AppTheme.Base">
<!-- Customize your theme here. -->
<item name="vpiTabPageIndicatorStyle">@style/TinderLiker.IndicatorStyle</item>
</style>
<style name="AppTheme.Base" parent="Theme.AppCompat">
<item name="windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<!-- Actionbar color -->
<item name="colorPrimary">@color/red</item>
<!--Status bar color-->
<item name="colorPrimaryDark">@color/red</item>
<!--Window color-->
<item name="android:windowBackground">@android:color/white</item>
</style>
<style name="TinderLiker.IndicatorStyle" parent="@style/Widget.TabPageIndicator">
<item name="android:background">@drawable/custom_tab_indicator</item>
<item name="android:divider">@drawable/custom_tab_indicator_divider</item>
<item name="android:gravity">center</item>
<item name="android:textColor">@drawable/text_color_viewpager_tab</item>
<item name="android:textSize">18sp</item>
<item name="android:textStyle">normal</item>
</style>
</resources>

3.定义toolbar.xml 的布局视图

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize">
</android.support.v7.widget.Toolbar>

4.定义activity_main.xml 的布局视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<include
layout="@layout/toolbar"
android:id="@+id/toolbar" />
<!-- A DrawerLayout is intended to be used as the top-level content view using match_parent for both width and height to consume the full space available. -->
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/toolbar">
<!--
As the main content view, the view below consumes the entire
space available using match_parent in both dimensions.
-->
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!--
android:layout_gravity="start" tells DrawerLayout to treat
this as a sliding drawer on the left side for left-to-right
languages and on the right side for right-to-left languages.
The drawer is given a fixed width in dp and extends the full height of
the container. A solid background is used for contrast
with the content view.
-->
<ListView
android:id="@+id/left_drawer"
android:layout_width="285dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#fff" />
</android.support.v4.widget.DrawerLayout>
</RelativeLayout>

5.MainActivity.java的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
package com.ilegendsoft.toprankapps.tinderliker.activities;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.Volley;
import com.ilegendsoft.ilsalib.ilsautils.IlsUtilsManager;
import com.ilegendsoft.ilsalib.ilsautils.log.Logger;
import com.ilegendsoft.ilsalib.ilsazcloud.item.CloudyParameter;
import com.ilegendsoft.ilsalib.ilsazcloud.utils.ZCloudAccountUtils;
import com.ilegendsoft.ilsalib.ilsazcloud.utils.ZCloudCloudyParameterUtil;
import com.ilegendsoft.toprankapps.tinderliker.R;
import com.ilegendsoft.toprankapps.tinderliker.adapters.DrawerAdapter;
import com.ilegendsoft.toprankapps.tinderliker.fragments.DiscoverFragment;
import com.ilegendsoft.toprankapps.tinderliker.fragments.LocationFragment;
import com.ilegendsoft.toprankapps.tinderliker.fragments.SettingsFragment;
import com.ilegendsoft.toprankapps.tinderliker.fragments.TinderLikerFragment;
import com.ilegendsoft.toprankapps.tinderliker.fragments.TindersFragment;
import com.ilegendsoft.toprankapps.tinderliker.items.Drawer;
import java.lang.reflect.Field;
import java.util.ArrayList;
public class MainActivity extends BaseActivity {
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ActionBar mActionBar;
private ActionBarDrawerToggle mDrawerToggle;
private DrawerAdapter mDrawerAdapter;
private CharSequence mDrawerTitle;
private CharSequence mTitle;
private ArrayList<Drawer> mDrawers;
private FragmentManager mFragmentManager;
private TinderLikerFragment mTinderLikerFragment;
private DiscoverFragment mDiscoverFragment;
private TindersFragment mTindersFragment;
private LocationFragment mLocationFragment;
private SettingsFragment mSettingsFragment;
private long mLastBackPressedTime = 0;
private Fragment mCurrentFragment;
private Toolbar mToolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
if (savedInstanceState == null) {
selectItem(0);
}
checkCloudyParameter();
}
private void checkCloudyParameter() {
ZCloudCloudyParameterUtil.getCloudyParameterRequest(Volley.newRequestQueue(this),
ZCloudAccountUtils.getAccessTokenNoLogin(this), this,
new ZCloudCloudyParameterUtil.OnSuccessListener() {
@Override
public void onSuccess(CloudyParameter cloudyParameter) {
IlsUtilsManager.getInstance().check(MainActivity.this,
cloudyParameter.getAlternative(), cloudyParameter.getVersion(), 0, null);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
IlsUtilsManager.getInstance().check(MainActivity.this, "", "", 0, null);
Logger.toast(MainActivity.this, R.string.network_error);
}
});
}
private void init() {
mTitle = mDrawerTitle = getTitle();
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
mActionBar = getSupportActionBar();
// enable ActionBar app icon to behave as action to toggle nav drawer
//mActionBar.setDisplayHomeAsUpEnabled(true);
//mActionBar.setHomeButtonEnabled(true);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
mDrawerList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
initDrawerListData();
mDrawerAdapter = new DrawerAdapter(this, mDrawers);
mDrawerList.setAdapter(mDrawerAdapter);
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow,
GravityCompat.START);
// ActionBarDrawerToggle ties together the the proper interactions
// between the sliding drawer and the action bar app icon
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
mToolbar, R.string.drawer_open,
R.string.drawer_close) {
public void onDrawerClosed(View view) {
mActionBar.setTitle(mTitle);
supportInvalidateOptionsMenu();
}
public void onDrawerOpened(View drawerView) {
mActionBar.setTitle(mDrawerTitle);
supportInvalidateOptionsMenu();
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
initMainFragment();
}
private void initMainFragment() {
mFragmentManager = getSupportFragmentManager();
mTinderLikerFragment = TinderLikerFragment.newInstance();
mDiscoverFragment = DiscoverFragment.newInstance();
mTindersFragment = TindersFragment.newInstance();
mLocationFragment = LocationFragment.newInstance();
mSettingsFragment = SettingsFragment.newInstance();
}
private void initDrawerListData() {
mDrawers = new ArrayList<Drawer>();
String[] drawerNames = getResources().getStringArray(
R.array.drawer_array);
for (int i = 0; i < drawerNames.length; i++) {
Drawer drawer = new Drawer();
switch (i) {
case 0:
drawer.setImageResourceId(R.drawable.btn_navgationdrawer_tinderlike);
break;
case 1:
drawer.setImageResourceId(R.drawable.btn_navgationdrawer_tinders);
break;
case 2:
drawer.setImageResourceId(R.drawable.btn_navgationdrawer_discoversetting);
break;
case 3:
drawer.setImageResourceId(R.drawable.btn_navgationdrawer_locationsetting);
break;
case 4:
drawer.setImageResourceId(R.drawable.btn_navgationdrawer_setting);
break;
default:
break;
}
drawer.setName(drawerNames[i]);
mDrawers.add(drawer);
}
}
/* Called whenever we call invalidateOptionsMenu() */
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// If the nav drawer is open, hide action items related to the content
// view
boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
if (drawerOpen) hideSoftKeyBoard();
if (mCurrentFragment == mTinderLikerFragment) {
int[] ids = {R.id.action_auto};
controlMenuDisplay(menu, drawerOpen, ids);
} else if (mCurrentFragment == mLocationFragment) {
int[] ids = {R.id.action_search};
controlMenuDisplay(menu, drawerOpen, ids);
}
return super.onPrepareOptionsMenu(menu);
}
@Override
protected void onResume() {
super.onResume();
}
private void hideSoftKeyBoard() {
InputMethodManager inputManager = (InputMethodManager) this
.getSystemService(Context.INPUT_METHOD_SERVICE);
// check if no view has focus:
View view = this.getCurrentFocus();
if (view == null)
return;
inputManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
private void controlMenuDisplay(Menu menu, boolean drawerOpen, int[] ids) {
for (int id : ids) {
MenuItem item = menu.findItem(id);
if (item != null) {
item.setVisible(!drawerOpen);
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// The action bar home/up action should open or close the drawer.
// ActionBarDrawerToggle will take care of this.
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
private class DrawerItemClickListener implements
ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
selectItem(position);
}
}
private void selectItem(int position) {
switch (position) {
case 0:
mCurrentFragment = mTinderLikerFragment;
break;
case 1:
mCurrentFragment = mTindersFragment;
break;
case 2:
mCurrentFragment = mDiscoverFragment;
break;
case 3:
mCurrentFragment = mLocationFragment;
break;
case 4:
mCurrentFragment = mSettingsFragment;
break;
default:
break;
}
mFragmentManager.beginTransaction()
.replace(R.id.content_frame, mCurrentFragment).commit();
// update selected item and title, then close the drawer
setTitle(mDrawers.get(position).getName());
// handle the not smooth drawer.
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mDrawerLayout.closeDrawer(mDrawerList);
}
}, 200);
}
@Override
public void setTitle(CharSequence title) {
mTitle = title;
TextView titleView = getToolBarTitleTextView();
if (titleView != null) {
Typeface font = Typeface.createFromAsset(getAssets(), "SansitaOne.ttf");
titleView.setTypeface(font);
titleView.setText(mTitle);
titleView.setTextSize(23);
titleView.setTextColor(0xDDFFFFFF);
} else {
mActionBar.setTitle(mTitle);
}
}
private TextView getToolBarTitleTextView() {
TextView titleTextView = null;
try {
Field f = mToolbar.getClass().getDeclaredField("mTitleTextView");
f.setAccessible(true);
titleTextView = (TextView) f.get(mToolbar);
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
}
return titleTextView;
}
/**
* When using the ActionBarDrawerToggle, you must call it during
* onPostCreate() and onConfigurationChanged()...
*/
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggls
mDrawerToggle.onConfigurationChanged(newConfig);
}
@Override
public void onBackPressed() {
if (System.currentTimeMillis() - mLastBackPressedTime < 2000) {
super.onBackPressed();
} else {
mLastBackPressedTime = System.currentTimeMillis();
Logger.toast(this, R.string.exit);
}
}
}

android 自定义toolBar上的 action item

1.先看一下效果

2.自定义的view,action_view_auto_like.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageButton
android:id="@+id/action_auto_like_button"
android:layout_width="70dp"
android:layout_height="30dp"
android:scaleType="centerInside"
android:background="@android:color/transparent"
android:src="@drawable/btn_nav_autoliker">
</ImageButton>
</FrameLayout>

3.自定义的menu,menu_tinder_liker.xml

1
2
3
4
5
6
7
8
9
10
11
12
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tinder="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_auto"
android:actionLayout="@layout/action_view_auto_like"
android:icon="@drawable/btn_nav_autoliker"
android:orderInCategory="1"
android:title="@string/action_auto_like"
tinder:actionLayout="@layout/action_view_auto_like"
tinder:showAsAction="always"/>
</menu>

4.在fragment 中配置

现在onCreateView中加上setHasOptionsMenu(true);,让系统在fragment中初始化menu。

1
2
3
4
5
6
7
8
9
10
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_tinder_like, menu);
MenuItem searchItem = menu.findItem(R.id.action_auto);
FrameLayout layout = (FrameLayout) MenuItemCompat
.getActionView(searchItem);
layout.findViewById(R.id.action_auto_like_button)
.setOnClickListener(this);
super.onCreateOptionsMenu(menu, inflater);
}

然后在onClick中加入你的逻辑。

Android studio 1.0 RC更新

build.gradle改动:

1.buildTypes->release->runProguard()

这里是runProguard()方法被替换了,
把runProguard改为minifyEnabled即可。

2.productFlavors->google->packageName=’com.toprankapps.snapgrab’

这里是packageName=’com.alinaapps.privatealbum’已经被替换,
改成applicationId ‘com.alinaapps.privatealbum’。

3.buildTypes->applicationVariants.all

这里的applicationVariants.all方法中packageApplication这个属性被取消了。

原来:

applicationVariants.all { variant ->
apk = variant.packageApplication.outputFile;
newName = apk.name.replace(“.apk”, “-v” + defaultConfig.versionName + “.apk”);
newName = newName.replace(“app”, “PrivateAlbum”);
variant.packageApplication.outputFile = new File(apk.parentFile, newName);
if (variant.zipAlign) {
variant.zipAlign.outputFile = new File(apk.parentFile, newName.replace(“-unaligned”, “”));
}
}

现在:

applicationVariants.all{ variant ->
def apk = variant.outputs[0].outputFile;
def versionName = android.defaultConfig.versionName;
def newName = “”;
if (variant.buildType.name == “release”) {
newName = apk.name.replace(“.apk”, “_V” + versionName + “_release.apk”);
} else {
newName = apk.name.replace(“.apk”, “_V” + versionName + “_debug.apk”);
}
newName = newName.replace(“-“ + variant.buildType.name, “”);
variant.outputs[0].outputFile = new File(apk.parentFile, newName);
if (variant.outputs[0].zipAlign) {
variant.outputs[0].zipAlign.outputFile = new File(apk.parentFile, newName.replace(“-unaligned”, “”));
}
}

4.android studio中的plugin也发生了改变

这里是将”apply plugin: ‘android’,
改为”apply plugin: ‘com.android.application’

5. resConfigs,shrinkResources 去除无用资源

defaultConfig {

// …

resConfigs “en”, “de”, “fr”, “it”
resConfigs “nodpi”, “hdpi”, “xhdpi”, “xxhdpi”, “xxxhdpi”
}

保留指定资源目录,去除库项目可能存在的冗余资源

shrinkResources 使用注意项目是否存在反射获取资源

6. Java SDK 版本建议用 7 支持 Android API Level 8 及以上

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}

Added support for Java 7 language features like multi-catch, try-with-resources, and the diamond operator.

Try-with-resources requires minSdkVersion 19; the rest of the new language features require minSdkVersion 8 or higher.

项目规模较小可以考虑采用 JDK 8,lambda 有助于代码简洁,配合 Rxjava 是极好的

使用见 https://github.com/evant/gradle-retrolambda

Android Studio 对 lambda 的支持也是极好的

gradle 编译命令

Android Studio的项目中自带了gradle 编译环境,如下图:

gradle wrapper 避免了gradle 因版本不同引发的种种问题,使你不用在本地安装gradle 就可以执行编译命令。

  1. server:CardViewSample samzhao$ ./gradlew clean assemble

    先清理以前编译过的文件再编译,耗时最长。我这次编译用了

    BUILD SUCCESSFUL

    Total time: 32.394 secs

  2. server:CardViewSample samzhao$ ./gradlew assemble

    不清理,采用以前编译好的,不需要重新编译的文件进行编译。许多编译项目后面都跟有UP-TO-DATE,如:
    :app:compileDebugAidl UP-TO-DATE
    这主要是更新了文件的时间戳,没有进行重新编译。这次时间明显减少了:

    BUILD SUCCESSFUL

    Total time: 8.394 secs

  3. server:CardViewSample samzhao$ ./gradlew assemble --daemon

    加上--daemon后缀就不用每次编译时都把gradle重新加载到内存了,第一次用--daemon,编译时间还是8秒多,但第二次运行就快很多了,如下:

    BUILD SUCCESSFUL

    Total time: 4.021 secs

编译好的apk在如下位置:

其实cleanassemble都是gradletask

查询tasks,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
server:CardViewSample samzhao$ ./gradlew -q tasks
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project
signingReport - Displays the signing info for each variant
Build tasks
-----------
assemble - Assembles all variants of all applications and secondary packages.
assembleDebug - Assembles all Debug builds
assembleDebugTest - Assembles the Test build for the Debug build
assembleRelease - Assembles all Release builds
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
clean - Deletes the build directory.
Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]
Help tasks
----------
dependencies - Displays all dependencies declared in root project 'CardViewSample'.
dependencyInsight - Displays the insight into a specific dependency in root project 'CardViewSample'.
help - Displays a help message
projects - Displays the sub-projects of root project 'CardViewSample'.
properties - Displays the properties of root project 'CardViewSample'.
tasks - Displays the tasks runnable from root project 'CardViewSample' (some of the displayed tasks may belong to subprojects).
Install tasks
-------------
installDebug - Installs the Debug build
installDebugTest - Installs the Test build for the Debug build
uninstallAll - Uninstall all applications.
uninstallDebug - Uninstalls the Debug build
uninstallDebugTest - Uninstalls the Test build for the Debug build
uninstallRelease - Uninstalls the Release build
Verification tasks
------------------
check - Runs all checks.
connectedAndroidTest - Installs and runs the tests for Build 'debug' on connected devices.
connectedCheck - Runs all device checks on currently connected devices.
deviceCheck - Runs all device checks using Device Providers and Test Servers.
lint - Runs lint on all variants.
lintDebug - Runs lint on the Debug build
lintRelease - Runs lint on the Release build
To see all tasks and more detail, run with --all.
server:CardViewSample samzhao$

参考链接:

http://blog.stylingandroid.com/tag/gradle-2/