禁止当前 Activity截图
<pre>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
setContentView(R.layout.activity_main);
}
</pre>
获取当前输入法
<pre>
String currentInputmethod = Settings.Secure.getString(getContentResolver(),Settings.Secure.DEFAULT_INPUT_METHOD);
LogUtil.e("currentInputmethod = "+currentInputmethod);
E/LogUtil: [ (MainActivity.java:35)#getInputMethod ] currentInputmethod = com.tencent.qqpinyin/.QQPYInputMethodService
</pre>
获取手机里面的所有输入法
<pre>
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
// 取得当前所有的输入法
List<InputMethodInfo> infos = imm.getInputMethodList();
for (InputMethodInfo info : infos) {
LogUtil.e("输入法包名:" + info.getPackageName());
}
? E/LogUtil: [ (MainActivity.java:32)#getInputMethod ] 输入法包名:com.tencent.qqpinyin
? E/LogUtil: [ (MainActivity.java:32)#getInputMethod ] 输入法包名:com.meizu.flyme.input
? E/LogUtil: [ (MainActivity.java:32)#getInputMethod ] 输入法包名:com.hathy.simplekeyboard
? E/LogUtil: [ (MainActivity.java:32)#getInputMethod ] 输入法包名:com.alex.keyboardview
</pre>
安卓跟H5交互之基础篇 方式1 - 有漏洞
<pre>
@SuppressLint("JavascriptInterface")
@Override
public void onCreateData(Bundle bundle) {
webView = findView(wv);
HtmlHelper.getInstance().webView(webView).javaScriptEnabled(true).applyCacheModeByNetWork();
webView.loadUrl("file:///android_asset/index.html");
webView.addJavascriptInterface(new JSInterface(), "MobileDevice");
}
@Override
public void onClick(View v, int id) {
super.onClick(v, id);
if(R.id.bt_1 == id){
String info = "来自手机内的内容!!!"+new Random().nextInt(100);
webView.loadUrl("javascript:paramsFromMobile('"+info+"')");
}
}
public class JSInterface{
@JavascriptInterface
public void callShortToast(String text){
ToastUtil.shortCenter(text);
LogUtil.e("dddd");
}
}
</pre>
安卓跟H5交互之基础篇 方式2 - 较安全
<pre>
public class MainActivity extends HJActivity {
private WebView webView;
@Override
public int getLayoutResId() {
return R.layout.activity_main;
}
@SuppressLint("JavascriptInterface")
@Override
public void onCreateData(Bundle bundle) {
webView = findView(wv);
WebViewHelper.getInstance(webView).javaScriptEnabled(true).applyCacheModeByNetWork();
webView.loadUrl("file:///android_asset/index.html");
webView.setWebChromeClient(new MySimpleWebChromeClient());
webView.setWebViewClient(new MySimpleWebViewClient());
}
@Override
public void onClick(View v, int id) {
super.onClick(v, id);
if (R.id.bt_1 == id) {
String info = "来自手机内的内容!!!" + new Random().nextInt(100);
webView.loadUrl("javascript:paramsFromNative('" + info + "')");
}
}
private final class MySimpleWebChromeClient extends SimpleWebChromeClient {
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
super.onJsPrompt(view, url, message, defaultValue, result);
LogUtil.w("url = " + url + " message = " + message + " defaultValue = " + defaultValue);
result.confirm();
return true;
}
}
private final class MySimpleWebViewClient extends SimpleWebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return false;
}
}
}
</pre>
view 自动生成随机的 id
<pre>
/**
* 作者:Alex
* 时间:2016/11/15 18:07
* 简述:
*/
public class ViewUtil {
private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
public static int generateViewId(View view) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
return View.generateViewId();
}
for (; ; ) {
final int result = sNextGeneratedId.get();
// aapt-generated IDs have the high byte nonzero; clamp to the range under that.
int newValue = result + 1;
if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
if (sNextGeneratedId.compareAndSet(result, newValue)) {
return result;
}
}
}
}
</pre>
<pre>
public class App extends BaseApp {
@Override
public void onCreate() {
super.onCreate();
onCreateFont();
}
private void onCreateFont() {
try {
Typeface typeface = Typeface.createFromAsset(BaseUtil.app().getAssets(), "font/simkai.ttf");
Field field_3 = Typeface.class.getDeclaredField("SANS_SERIF");
field_3.setAccessible(true);
field_3.set(null, typeface);
} catch (Exception e) {
LogUtil.e(e);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="alex_theme_no_title" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:typeface">sans</item>
</style>
</resources>
</pre>
真正的释放手动绑定点击事件
<pre>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResId());
onBindClickListener();
}
private void onBindClickListener() {
setOnCickListener(findViewById(android.R.id.content));
}
/**
* 设置点击事件
*
* @param view View
*/
private void setOnCickListener(View view) {
if (view == null) {
LogUtil.w("控件为空");
return;
}
view.setOnClickListener(this);
if (view instanceof ViewGroup) {
setOnCickListener((ViewGroup) view);
}
}
/**
* 设置点击事件
*
* @param viewGroup ViewGroup
*/
private void setOnCickListener(ViewGroup viewGroup) {
try {
viewGroup.setOnClickListener(this);
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View v = viewGroup.getChildAt(i);
if (v instanceof ViewGroup) {
setOnCickListener((ViewGroup) v);
} else if ((v != null) && (v instanceof View)) {
setOnCickListener(v);
}
}
} catch (Exception e) {
LogUtil.e(e.toString());
}
}
</pre>
Intent.FLAGS
<pre>
/**等于清单文件的 singTop, start之后,之前Activity的数据会保留不变 */
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
/**等于清单文件的 singTask, start之后,之前Activity的数据会保留不变 */
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
</pre>
点击跳转通讯录,选择联系人+手机号码
<pre>
@Override
public void onClick(View v)
{
super.onClick(v);
Intent intent = new Intent(Intent.ACTION_PICK,android.provider.ContactsContract.Contacts.CONTENT_URI);
//intent.setType("vnd.android.cursor.dir/phone");
startActivityForResult(intent, requestCode4Contact);
}
@Override
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(data == null){
return;
}
if(requestCode == requestCode4Contact){
//处理返回的data,获取选择的联系人信息
Uri uri=data.getData();
String[] contacts=ContactUtil.getPhoneContacts(uri,getContentResolver());
etName.setText(contacts[0]);
etPhone.setText(contacts[1]);
}
refreshEditText(etName);
refreshEditText(etPhone);
}
package org.alex.util;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
public class ContactUtil
{
/**
* [0] 姓名
* [1] 手机号
* */
public static String[] getPhoneContacts(Uri uri, ContentResolver contentResolver){
String[] textArray=new String[2];
String contactId = "";
String name = "";
String phone = "";
if(uri == null){
return textArray;
}
String url = uri.toString();
String contactSelectId = StringUtil.subString4End(url, "/");
//得到ContentResolver对象
//取得电话本中开始一项的光标
Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
//向下移动光标
while(cursor.moveToNext())
{
//取得联系人名字
int nameFieldColumnIndex = cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME);
name = cursor.getString(nameFieldColumnIndex);
//取得电话号码
contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
Cursor cursorPhone = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + contactId, null, null);
phone = "";
while(cursorPhone.moveToNext()) {
phone = cursorPhone.getString(cursorPhone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
LogUtil.e("phone = "+phone);
}
if(contactSelectId.equals(contactId)){
textArray[0] = name;
textArray[1] = trim(phone, ' ');
return textArray;
}
}
return textArray;
}
private static String trim(String text, char oldChar){
if(text == null){
return text;
}
char[] charArray = text.toCharArray();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < charArray.length; i++)
{
char indexChar = charArray[i];
if(indexChar !=oldChar){
builder.append(indexChar);
}
}
return builder.toString();
}
}
</pre>
判断用户是否安装 QQ、微信客户端
<pre>
/**
* 判断 用户是否安装微信客户端
*/
public static boolean isWeixinAvilible(Context context) {
final PackageManager packageManager = context.getPackageManager();// 获取packagemanager
List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);// 获取所有已安装程序的包信息
if (pinfo != null) {
for (int i = 0; i < pinfo.size(); i++) {
String pn = pinfo.get(i).packageName;
if (pn.equals("com.tencent.mm")) {
return true;
}
}
}
return false;
}
/\*\*
\* 判断 用户是否安装QQ客户端
\*/
public static boolean isQQClientAvailable(Context context) {
final PackageManager packageManager = context.getPackageManager();
List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);
if (pinfo != null) {
for (int i = 0; i < pinfo.size(); i++) {
String pn = pinfo.get(i).packageName;
LogUtils.e("pn = "+pn);
if (pn.equalsIgnoreCase("com.tencent.qqlite") || pn.equalsIgnoreCase("com.tencent.mobileqq")) {
return true;
}
}
}
return false;
}
</pre>
调起 QQ 和 1445607009 发起临时会话
<pre>String url="mqqwpa://im/chat?chat_type=wpa&uin=1445607009";
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
</pre>
Java 角度、正弦
<pre>
Math.sin((30f /180f) * Math.PI) = 0.5;
Math.asin(0.5000) * 180 / Math.PI = 30 °
</pre>
一些常用的状态码
100~199:指示信息,表示请求已接收,继续处理
200~299:请求成功,表示请求已被成功接收、理解、接受
300~399:重定向,要完成请求必须进行更进一步的操作
400~499:客户端错误,请求有语法错误或请求无法实现
500~599:服务器端错误,服务器未能实现合法的请求
Float 保留2 位小数
<pre>最简单的方法
float a = 123.2334f;
float b = (float)(Math.round(a*100))/100;
(这里的100就是2位小数点,如果要其它位,如4位,这里两个100改成10000,不足,不补0)
</pre>
<pre>
float apr = Float.parseFloat(JsonUtil.getString(result, "apr"));
/*构造方法的字符格式这里如果小数不足2位,会以0补足.*/
DecimalFormat decimalFormat=new DecimalFormat(".00");
String p= decimalFormat.format(apr);
LogUtil.e("apr = "+p);
</pre>
ListView的高度是自适应的,但是Ta有一个最大高度限制
<pre>
public class HeightListView extends ListView {
private Context context;
/\*\* listview 最大高度 \*/
private int maxHeight;
public int getListViewHeight() {
return maxHeight;
}
public void setMaxHeight(int height) {
this.maxHeight = (int) dp2Px(height);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (maxHeight > -1) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight,MeasureSpec.AT_MOST);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public HeightListView(Context context) {
super(context);
this.context = context;
}
public HeightListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
}
public HeightListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
/\*\*数据转换: dp---->px\*/
private float dp2Px(float dp)
{
if (context == null) {
return -1;
}
return dp \* context.getResources().getDisplayMetrics().density;
}
}
</pre>
监听ScrollView的滑动进度
<pre>
package github.alex.floattitlelayout;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;
/**
* Created by Alex on 2016/4/3.
* 可以被检测是否滑动到顶部的ScrollView
*/
public class ObservableScrollView extends ScrollView{
private OnScrollListener onScrollListener = null;
public ObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
@Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if (onScrollListener != null) {
onScrollListener.onScroll(this, x, y, oldx, oldy);
}
}
public interface OnScrollListener {
public void onScroll(ScrollView scrollView, int x, int y, int oldx, int oldy);
}
}
</pre>
屏蔽Edittext复制粘贴、剪切功能
<pre>
package github.alex.helper.inputfilter;
import android.text.InputFilter;
import android.text.Spanned;
import android.text.TextUtils;
import android.widget.EditText;
/**
* no cut paste
* 不可以粘贴、剪切的过滤器
* */
public class NcpInputFilter implements InputFilter
{
private EditText editText;
/**屏蔽粘贴功能*/
private boolean isNoPaste;
/**解决 剪切 和 粘贴 的 冲突*/
private boolean isAlexPaste;
public NccInputFilter(EditText editText) {
this.editText = editText;
isNoPaste = true;
isAlexPaste = false;
}
/**
* @param source 当前键入、键出 的 字符
* @param dest 所有的字符(除当前键入之外)
* @param start 新输入的字符串起始下标
* @param end 新输入的字符串终点下标
* @param dstart 原内容发生改变,改变的起点坐标
* @param dend 原内容发生改变,改变的终点坐标
* end > start 表示键入数据;end = 0 = start 表示键出数据
* @return 当前键入 键出的字符
* */
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)
{
if(dest == null){
return "";
}
if( TextUtils.isEmpty(source) && (dend-dstart > 1)){
/*屏蔽剪切*/
isAlexPaste = true;
editText.setText(dest);
editText.setSelection(dest.length());
return "";
}
if((!TextUtils.isEmpty(source)) && (end-start > 1) && isNoPaste){
if(isAlexPaste){
isAlexPaste = false;
return editText.getText();
}
/*屏蔽粘贴*/
return "";
}
return source;
}
}
</pre>
由于toolbar默认是居右的,title怎么实现居中么?
由于toolbar有一个16dp的content inset,会如图
用ViewFlipper 写垂直的跑马灯
<pre>
<ViewFlipper
android:id="@+id/vf"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#CCCCCC"
android:inAnimation="@anim/bottom_in"
android:outAnimation="@anim/bottom_out">
<TextView
style="@style/base_ww_hw_text_size_larger"
android:padding="16dp"
android:text="原生的跑马灯 01"/>
<TextView
style="@style/base_ww_hw_text_size_larger"
android:padding="16dp"
android:text="原生的跑马灯 02"/>
</ViewFlipper>
@Override
public void onCreateData(Bundle bundle) {
viewFlipper = findView(R.id.vf);
viewFlipper.setFlipInterval(1000);
viewFlipper.startFlipping();
new Handler() {
}.postDelayed(new Runnable() {
@Override
public void run() {
//原生的跑马灯 01
for (int i = 0; i < 3; i++) {
View view = LayoutInflater.from(activity).inflate(R.layout.item_flipper, null);
TextView textView = findView(view, R.id.tv);
setText(textView, "网络的跑马灯 " + i);
viewFlipper.addView(view);
}
}
}, 2000);
}
</pre>
animateLayoutChanges="true" 你还在自己写动画吗?
<pre>
<LinearLayout
android:animateLayoutChanges="true" >
<Button
android:text="add"
android:id="@+id/bt_add" />
</LinearLayout>
</pre>
android:clipToPadding="false" 你还在根据 position 来控制 间距吗?
<pre>
<ListView
android:clipToPadding="false"
android:paddingTop="16dp" />
</pre>
android:transcriptMode="normal"
<pre>
默认情况下,当添加的 Item 超出 ListView 的范围后,ListView 并没有刷新让最新一条显示出来。
而在 qq/微信 聊天中,发新的消息后会自动滚动显示出最下面的一条信息。
这个最适合做聊天布局
AbsListView.TRANSCRIPT_MODE_DISABLED // 禁用
AbsListView.TRANSCRIPT_MODE_NORMAL // 正常状态
AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL // 总是滚动到最新一条
</pre>
修改输入法中回车按钮的显示内容
<pre>
/**
*
* IME_ACTION_SEARCH 搜索
* IME_ACTION_SEND 发送
* IME_ACTION_NEXT 下一步
* IME_ACTION_DONE 完成
*/
editText.setImeOptions(EditorInfo.IME_ACTION_SEARCH);
EditorInfo.IME_ACTION_DONE 可以和其他的标志一起组合使用来设置软键盘,比如:
editText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI|EditorInfo.IME_ACTION_DONE);
注意1:EditorInfo.IME_ACTION_DONE只有对android:singleLine="true"的EditText有效。
注意2:对于EditorInfo.IME_ACTION_DONE,有些输入法并不支持它,比如搜狗拼音输入法。
</pre>
监听输入法中的回车按钮
<pre>
/**
* 监听输入法按键
*
* */
editText.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
LogUtil.e("手指弹起, 回车按钮");
return true;
}
return false;
}
});
</pre>
android:duplicateParentState="true"
<pre>
让子View跟随其Parent的状态,如pressed等。
常见的使用场景是某些时候一个按钮很小,我们想要扩大其点击区域的时候通常会再给其包裹一层布局,将点击事件写到Parent上,
这时候如果希望被包裹按钮的点击效果对应的Selector继续生效的话,这时候duplicateParentState就派上用场了。
</pre>
android:clipChildren="false" 加在父容器上
tools:text="你是土鳖" 这是预览效果,运行时,看不到
屏蔽多点触碰
<pre>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:splitMotionEvents="false" />
</pre>
假装App 进程包活
<pre>
/*我能做到的,仅仅是让App存活的稍微久一点,仅此而已*/
/*android:alwaysRetainTaskState="true" 只在 入口在 Activity 有效*/
<activity
android:name=".ui.MainActivity"
android:alwaysRetainTaskState="true"
android:theme="@style/alex_theme_cold_start"
>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
public class MainActivity extends BaseActivity{
@Override
public void onBackPressed()
{
/*写在主页 , 按返回键返回桌面,不结束Activity*/
moveTaskToBack(true);
}
}
</pre>
Android 如何模拟返回键、菜单键、主页键?
<pre>
方法一:
Runtime runtime = Runtime.getRuntime();
runtime.exec('input keyevent ' + KeyEvent.KEYCODE_BACK);
方法二:
Instrumentation inst = new Instrumentation();
inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
</pre>