Android dev tips(update at 2015-11-26)

如何通过代码来重启Android手机?

最省事的是通过shell调用底层reboot命令,不过需要root权限。

	/**
	 * 执行一串shell命令
	 * 
	 * @param cmd
	 */
	private void executeShellCmd(String cmd) {

		try {
			// 申请获取root权限,这一步很重要,不然会没有作用
			Process process = Runtime.getRuntime().exec("su");
			// 获取输出流
			OutputStream outputStream = process.getOutputStream();
			DataOutputStream dataOutputStream = new DataOutputStream(
					outputStream);
			dataOutputStream.writeBytes(cmd);
			dataOutputStream.flush();
			dataOutputStream.close();
			outputStream.close();
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}

执行重启:

executeShellCmd("su -c reboot now");


Android通过代码唤醒屏幕并解锁

		//屏幕唤醒并解锁
		PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
		PowerManager.WakeLock wake = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP|PowerManager.SCREEN_DIM_WAKE_LOCK, "bright");
		//点亮屏幕
		wake.acquire();
		wake.release();
		
		KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
		KeyguardManager.KeyguardLock unlock = km.newKeyguardLock("unLock");
		//解锁
		unlock.disableKeyguard();


静态代码块和静态方法的区别

静态代码块是自动执行的,而静态方法是被调用的时候才执行的;

静态方法:如果我们在程序编写的时候需要一个不实例化对象就可以调用的方法,我们就可以使用静态方法,具体实现是在方法前面加上static,如下:

public static void method(){}


在使用静态方法的时候需要注意一下几个方面:

在静态方法里只能直接调用同类中其他的静态成员(包括变量和方法),而不能直接访问类中的非静态成员。这是因为,对于非静态的方法和变量,需要先创建类的实例对象后才可使用,而静态方法在使用前不用创建任何对象。(备注:静态变量是属于整个类的变量而不是属于某个对象的)

静态方法不能以任何方式引用this和super关键字,因为静态方法在使用前不用创建任何实例对象,当静态方法调用时,this所引用的对象根本没有产生。

静态程序块:当一个类需要在被载入时就执行一段程序,这样可以使用静态程序块。


在android4.0 应用程序在程序管理面被强制关闭无法开机启动【问题还未解决,遗留】

收不到 android.intent.action.BOOT_COMPLETED 消息 ,android.intent.action.USER_PRESENT消息也无法收到。http://bbs.csdn.net/topics/390313112


android:layout_gravity="bottom"不起作用问题?

对于 LinearLayout(父View)

当 android:orientation="vertical"  时, 只有水平方向的设置才起作用,垂直方向的设置不起作用。即:left,right,center_horizontal 是生效的。

当 android:orientation="horizontal" 时, 只有垂直方向的设置才起作用,水平方向的设置不起作用。即:top,bottom,center_vertical 是生效的。


MediaPlayer error (1, -2147483648)”问题?

http://www.cnblogs.com/frydsh/p/3443064.html



VideoView布局居中和MediaController布局问题

MediaController底部问题:

xml创建一个videoview:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <VideoView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

 如果videoview的外层容器的高度是match_parent或者fillparent的话。

视频控制器会和视频播放器分离如图:灰色为分离的部分:

所以在布局中设置videoview的时候最好给videoview外面单独套一个的容器。这个外层容器的高度要设定为wrap_content。这样就会把视频播放器和控制器很好的组合在一起,如下所示:

VideoView布局居中问题:

 默认情况下,如果视频分辨率小于屏幕分辨率,VideoView在播放视频时都是在左上角的,影响程序美观。
用下面的方法完美解决,视频居中播放:

<?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:layout_gravity="center"
    android:orientation="vertical" >

    <VideoView
        android:id="@+id/videoView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>
其实关键点就在于设置父Layout居中,比如RelativeLayout也可以:

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#000000" >

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true"
            android:orientation="vertical" >

            <VideoView
                android:id="@+id/video_test_video_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center" />
        </LinearLayout>
    </RelativeLayout>
起作用的就是:android:layout_centerHorizontal="true"; android:layout_centerVertical="true"


eclipse无法导出android签名包的问题

Export aborted because fatal lint errors were found.These are listed in the Problems view.Either fix these before running Export again,or turn off "Run full error check when exporting app" in the Android > Lint Error checking preference page.

解决办法:

Window-Preference-Android-Lint Error Checking




获取手机信号强度

通过系统服务获取TelephonyManager对象

TelephonyManager tel= (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
		tm.listen(new PhoneStateMonitor(), PhoneStateListener.LISTEN_SIGNAL_STRENGTHS|PhoneStateListener.LISTEN_SERVICE_STATE);

		/*
		 * tel.getNetworkOperator()
		 3G中国是460固定的,
		 中国移动的是 46000
		 中国联通的是 46001
		 中国电信的是 46003
		 *获取国别
		 tel.getSimCountryIso()
		 */
            tel.getDeviceId();//获取设备编号
            tel.getSimCountryIso();//获取SIM卡国别
            tel.getSimSerialNumber();//获取SIM卡序列号    
            tel.getSimState();//获取SIM卡状态
            tel.getDeviceSoftwareVersion();//获取软件版本
            tel.getNetworkOperator();//获取网络运营商代号
            tel.getNetworkOperatorName();//获取网络运营商名称
            tel.getPhoneType();//获取手机制式
            tel.getCellLocation().toString();//获取设备当前位置

实例化或是继承PhoneStateListener接口

	class PhoneStateMonitor extends PhoneStateListener {

		@Override
		public void onSignalStrengthsChanged(SignalStrength signalStrength) {
			super.onSignalStrengthsChanged(signalStrength);
			
			/*
		     * signalStrength.isGsm() 是否GSM信号 2G or 3G
		     * signalStrength.getCdmaDbm(); 联通3G 信号强度
		     * signalStrength.getCdmaEcio(); 联通3G 载干比
		     * signalStrength.getEvdoDbm(); 电信3G 信号强度
		     * signalStrength.getEvdoEcio(); 电信3G 载干比
		     * signalStrength.getEvdoSnr(); 电信3G 信噪比
		     * signalStrength.getGsmSignalStrength(); 2G 信号强度
		     * signalStrength.getGsmBitErrorRate(); 2G 误码率     
		     * 载干比 ,它是指空中模拟电波中的信号与噪声的比值
		     */
				
			System.out.println("IsGsm : " + signalStrength.isGsm()
					+ "  CDMA Dbm : " + signalStrength.getCdmaDbm()
					+ "  CDMA Ecio : " + signalStrength.getCdmaEcio()
					+ "  Evdo Dbm : " + signalStrength.getEvdoDbm()
					+ "  Evdo Ecio : " + signalStrength.getEvdoEcio()
					+ "  Gsm : "+ signalStrength.getGsmSignalStrength()
					+ "  Gsm rate : "+ signalStrength.getGsmBitErrorRate());
		}
	}
服务状态改变

		@Override
		public void onServiceStateChanged(ServiceState serviceState) {
			super.onServiceStateChanged(serviceState);
			/*
			 * ServiceState.STATE_EMERGENCY_ONLY 仅限紧急呼叫
			 * ServiceState.STATE_IN_SERVICE 信号正常
			 * ServiceState.STATE_OUT_OF_SERVICE 不在服务区
			 * ServiceState.STATE_POWER_OFF 断电
			 */
			int pos = serviceState.getState();
			Log.v(TAG, "state change===" + pos);

			switch (pos) {
			case ServiceState.STATE_EMERGENCY_ONLY:
				break;
			case ServiceState.STATE_IN_SERVICE:
				break;
			case ServiceState.STATE_OUT_OF_SERVICE:
				break;
			case ServiceState.STATE_POWER_OFF:
				break;
			}

		}


WebView开启自由缩放以及PostUrl

		mWebView = (WebView) findViewById(R.id.app_webview);
		//设置支持JavaScript,默认不支持
		mWebView.getSettings().setJavaScriptEnabled(true);
		// 缩放开关 设置此属性,仅支持双击缩放,不支持触摸缩放(在android4.0是这样,其他平台没试过)
		mWebView.getSettings().setSupportZoom(true);
		// 设置是否可缩放
		mWebView.getSettings().setBuiltInZoomControls(true);
		// 无限缩放
		mWebView.getSettings().setUseWideViewPort(true);
		// 设置Web视图缩小内容以适应屏幕宽度
		mWebView.getSettings().setLoadWithOverviewMode(true);

正常加载URL

mWebView.loadUrl("http://www.test.com/login");


点击URL视图上的某个按钮或组件时,提示打开方式(使用系统自带浏览器或其它工具),如何在当前界面打开新的窗口内容?


解决办法:

		mWebView.setWebViewClient(new WebViewClient() {
			@Override
			public boolean shouldOverrideUrlLoading(WebView view, String url) {

				view.loadUrl(url);// 在当前的webview中跳转到新的url
				return true;
			}
		});

WebView 以 POST 方式加载URL:

		JSONObject json = new JSONObject();
		try {
			json.put("username", "mryang");
			json.put("password", "222222");
			json.put("role", "0");
		} catch (JSONException e) {
			e.printStackTrace();
		}
		mWebView.postUrl("http://www.test.com/login/wap",
				EncodingUtils.getBytes(json.toString(), "UTF-8"));

传送门,这里有关WebView的总结更全面,我就不重复造车轮了~ http://blog.csdn.net/caesardadi/article/details/8530477


EditText如何定位光标位置?

代码:edittext.setSelection(int);
例:
et.setText(content);//设置EditText控件的内容
et.setSelection(content.length());//将光标移至文字末尾


Android中GridView、ListView的getChildAt方法返回null (通过getChildAt方法取得AdapterView中第n个Item)

ListView或GridView的方法:getChildAt(int index)

一开始以为传入一个绝对的position(就是adapter的第几个item)就可以返回该position的View。但是GridView和ListView对View采用回收机制,简单的说明一下就是:如果屏幕最多可以显示n个子View,那么内存中其实只有n个View,当我们在滚动时,第(n+1)个View复用第1个View,依次类推。所以在GridView和ListView中,getChildAt ( int position ) 方法中position指的是当前可见区域的第几个元素。

				//如果你要获得GridView的第n个View,那么position就是index减去第一个可见View的位置
						int firstVisablePosition = ((ListView) view)
								.getFirstVisiblePosition();
						View itemView = ((ListView) view).getChildAt(index
								- firstVisablePosition);

简单实现模仿android 4.0 通知栏动画listview 滑动删除item

左右滑动删除Item,类似与iPhone上滑动删除一样的效果,只是比较粗糙,展示一下实现方式,直接贴上代码:

Activity中:

		listView = getListView();
		array = new ArrayList<String>();
		String aa[] = { "item0", "item1", "item2", "item3", "item4", "item5",
				"item6", "item7", "item8", "item9", "item10", "item11",
				"item12", "item13", "item14", "item15", "item16" };
		for (int i = 0; i < aa.length; i++) {
			array.add(aa[i]);
		}
		adapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_list_item_1, array);
		listView.setAdapter(adapter);

		/**
		 * 添加listview滑动接听
		 */
		listView.setOnTouchListener(new OnTouchListener() {
			float x, y, upx, upy;

			public boolean onTouch(View view, MotionEvent event) {
				if (event.getAction() == MotionEvent.ACTION_DOWN) {
					x = event.getX();
					y = event.getY();
				}
				if (event.getAction() == MotionEvent.ACTION_UP) {
					upx = event.getX();
					upy = event.getY();
					int startPosition = ((ListView) view).pointToPosition(
							(int) x, (int) y);
					int endPosition = ((ListView) view).pointToPosition(
							(int) upx, (int) upy);
					if (startPosition == endPosition && Math.abs(x - upx) > 10) {
						int firstVisablePosition = ((ListView) view)
								.getFirstVisiblePosition();
						View itemView = ((ListView) view)
								.getChildAt(startPosition
										- firstVisablePosition);
						removeListItem(itemView, startPosition);
					}
				}
				return false;
			}

		});

removeListItem:

	/**
	 * 删除item,并播放动画
	 * 
	 * @param rowView
	 *            播放动画的view
	 * @param positon
	 *            要删除的item位置
	 */
	protected void removeListItem(View itemView, final int position) {
		final Animation animation = AnimationUtils.loadAnimation(
				getApplicationContext(), R.anim.item_anim);
		animation.setAnimationListener(new AnimationListener() {
			public void onAnimationStart(Animation animation) {
			}

			public void onAnimationRepeat(Animation animation) {
			}

			public void onAnimationEnd(Animation animation) {
				//动画播放完毕时,array数据源删除相应坐标下的数据,adapter删除相应下item的View
				array.remove(array.get(position));
				adapter.notifyDataSetChanged();
				// animation.cancel();
			}
		});

		itemView.startAnimation(animation);
	}
item_anim.xml

<?xml version="1.0" encoding="utf-8"?> 
<translate xmlns:android="http://schemas.android.com/apk/res/android" 
    android:duration="2000" 
    android:fromXDelta="0" 
    android:fromYDelta="0" 
    android:toXDelta="800" 
    android:toYDelta="0" /> 
效果图:



消除manifest中权限警告:Permission is only granted to system apps

在AndroidManifest.xml中声明了一些权限,如:
<uses-permission android:name="android.permission.WRITE_APN_SETTINGS" /> 就会报错:Permission is only granted to system apps
此类权限仅授予系统级应用,可以修改下Link Error Checking项的安全级别:
In Eclipse: Window -> Preferences -> Android -> Lint Error Checking
在ID列表中,找到ID =  ProtectedPermission设置Severity低于Error,比如Warning级别就ok了


Unable to execute dex: Multiple dex files define Landroi 


运行时直接崩溃提示这个错误,一般情况下,跟libs里的jar文件有关系,同一文件(包名)导入了2个,有重复的,可以查看工程build path,尤其是Android Dependencies一定有重复引入的.jar包,解决的方法是在libs删除重复的jar即可。


根据屏幕尺寸(密度)设置文字大小的方式

有两种,的一种是根据屏幕尺寸,这种方法相当于自己随意定制,只是根据读取的屏幕宽度来判断取值,代码如下:

	/**
	 * 如有需要可以添加上screenHeight
	 * @param screenWidth
	 * @return
	 */
	public static int adjustFontSize(int screenWidth) {
		
		if (screenWidth <= 240) { // 240X320 屏幕

			return 10;

		} else if (screenWidth <= 320) { // 320X480 屏幕

			return 14;

		} else if (screenWidth <= 480) { // 480X800 或 480X854 屏幕

			return 24;

		} else if (screenWidth <= 540) { // 540X960 屏幕

			return 26;

		} else if (screenWidth <= 800) { // 800X1280 屏幕

			return 30;

		} else { // 大于 800X1280

			return 30;

		}
	}
另一种是根据屏幕密度,如果是布局(xml)上的文字,可以直接使用 sp 作为单位,这个单位的 textSize 是密度单位跟长度的 dp(dip) 类似,自己设置合适大小后,换一个布局(屏幕尺寸)也会随之得到改变。

如果要用 Canvas 之类的画出文本,这个时候需要指定文本的大小为 px 单位。

1,在当前的设备上调节字体至合适的大小
比如:640x480 的屏幕,text 的文本大小为 10

2, 获取当期设备的屏幕像素宽度
在 Activity 里面使用
int screenWidth = getResources().getDisplayMetrics().widthPixels;
3,适当的计算一下
640x480 的屏幕用 10 的文本大小,如果在960或是1080宽度的屏幕下呢?
float textSize = dm.widthPixels / 480.0f * 10;

这样就实现了设定一个中间值,然后根据不同屏幕宽度,来计算文字的大小。


图标引擎,AchartEngine的柱状图一些属性的设置:

1. 修改背景色或设置背景图片

// 设置是否应用背景颜色
render.setApplyBackgroundColor(true);
// 设置图表整个边框颜色
render.setMarginsColor(Color.WHITE);
render.setBackgroundColor(Color.WHITE);

2. setAxisTitleTextSize(16);// 设置坐标轴标题文本大小
3. setChartTitleTextSize(20); // 设置图表标题文本大小
4. setLabelsTextSize(15); // 设置轴标签文本大小
5. setLegendTextSize(15); // 设置图例文本大小
6. renderer.setChartTitle( "个人收支表");//设置柱图名称
7. renderer.setXTitle( "名单" );//设置X轴名称
8. renderer.setYTitle( "金额" );//设置Y轴名称
9. renderer.setXAxisMin(0.5);//设置X轴的最小值为0.5
10.renderer.setXAxisMax(5.5);//设置X轴的最大值为5
11.renderer.setYAxisMin(0);//设置Y轴的最小值为0
12.renderer.setYAxisMax(500);//设置Y轴最大值为500
13.renderer.setDisplayChartValues(true);//设置是否在柱体上方显示值
14.renderer.setShowGrid(true);//设置是否在图表中显示网格
15.renderer.setXLabels(0);//设置X轴显示的刻度标签的个数
16.如果想要在X轴显示自定义的标签,那么首先要设置renderer.setXLabels(0);  如果不设置为0,那么所设置的Labels会与原X坐标轴labels重叠
其次我们要renderer.addTextLabel()循环添加
for(int i=0;i<13;i++){
         renderer.addTextLabel(i+1,years[i]); //循环添加Xlabel其中显示的label放在years数组中
       }
17.renderer.setXLabelsAlign(Align.RIGHT);//设置刻度线与X轴之间的相对位置关系
18.renderer.setYLabelsAlign(Align.RIGHT);//设置刻度线与Y轴之间的相对位置关系
19.renderer.setZoomButtonsVisible(true);//设置可以缩放
20.renderer.setPanLimits(newdouble[] { 0, 20, 0, 140 });//设置拉动的范围
21.renderer.setZoomLimits(newdouble[] { 0.5, 20, 1, 150 });//设置缩放的范围
22.renderer.setRange(newdouble[]{0d, 5d, 0d, 100d}); //设置chart的视图范围
23.renderer.setFitLegend(true);// 调整合适的位置
24.renderer.setClickEnabled(true)//设置是否可以滑动及放大缩小;


java Switch中的case后面加上大括号({})和不加大括号的区别

出处:http://www.cnblogs.com/tony-yang-flutter/p/3601056.html

下面给出三段代码大家看一下有什么不同以及哪段代码能够编译通过那段代码编译不能通过,为什么?(Why?)

代码片段一:

int value = 10;
        switch (value) {
        case 1:
            int value1 = 5;
            break;
        case 2:
            int value1 = 6;
            break;
        }
代码片段二:

int value = 10;
        // switch
        switch (value) {
        case 1: {
            int value1 = 5;
            break;
        }
        case 2: 
            int value1 = 8;
            break;
        }
代码片段三:

int value = 10;
        // switch
        switch (value) {
        case 1: {
            int value1 = 5;
            break;
        }
        case 2: {
            int value1 = 8;
            break;
        }
        }
    }
结果: 片段1 不能通过编译器的编译, 片段2片段3可以通过编译器的编译
想要弄明白这个问题,就要先弄明白Java总变量的作用域问题。我们都知道在java(其他语言也是如此例如:C/C++/java/C#等)中同一个作用域中不能有两个相同的变量名称,(因为如果两个变量名相同容易造成混淆,编译器不知道到底该用那个变量)。

片段1中的两个value1的作用域都在switch的{}内,根据 “同一个作用域中不能有两个名称相同的变量名” 的出片段1肯定不能编译通过
          
        片段2中的两个value1的作用域分别为:

  case 1:中的value1的作用域为case1的{}内
          case 2:中的value1的作用域为switch的{}内

两个变量的作用域不同,当然可以定义相同的变量名称了。
         
        片段3中的两个value的作用域分别为:

  case 1 中的value1的作用域为case 1的{}内,
          case 2 中的value1的作用域为case 2的{}内,

        所以两个作用域互不交叉,因此编译能够通过。
          

注意:switch()的{}外面有一个value变量名,此时你不能在case 的{}中定义变量名称为value的变量,因为switch()的{}外的value变量的作用域包含了case{}内的作用域,如下所示:

	private void switchTest() {
		int value = 12;
		switch (32) {
		case 2: {
			int value1 = 23;
			break;
		}
		case 3: {
			int value = 32;//这个value是错误的,在method已经定义
			break;
		}
		}
	}
但是,如果在整个类定义一个value,相当于全局变量(常量),会是什么样的呢?

	private int vlaue;
	private void switchTest() {
		//int value = 12;
		switch (32) {
		case 2: {
			int value = 23;//这个value是可以用的,作用域在case2{}内,和全局value作用域不一样
			break;
		}
		case 3: //即便不加括号,同样可以使用,作用域相当于在switchTest方法中
			int value = 32;
			break;
		
		}
	}

总结使用场合:为了防止在case块中定义相同变量名而出现编译不同过的情况,我们通常会加上{}(目前就知道这种情况,如果以后遇到了再补充)。所以通常情况下是不必加上{}的。该怎么做就怎么做。


ListView快速开发常用方法以及常见问题

【1】如果给ListView设置了背景图片:android:background="@drawable/bg",当你拖动,或者点击list空白位置的时候发现ListItem都变成黑色的话,原因是由于:默认的ListItem背景是透明的,而ListView的背景是固定不变的,所以在滚动条滚动的过程中如果实时地去将当前每个Item的显示内容跟背景进行混合运算,所以android系统为了优化这个过程用,就使用了一个叫做android:cacheColorHint的属性,在黑色主题下默认的颜色值是#191919,所以就出现了类似黑色的效果。

解决办法:如果只是换背景的颜色的话,可以直接指定android:cacheColorHint为你所要的颜色,如果你是用图片做背景的话,那也只要将android:cacheColorHint指定为透明(#00000000)就可以了。

【2】不想使用listview自带的divider样式,自定义两种方式:

           android:divider="#f1f1f1"
            android:dividerHeight="1dp"
如果divider="@drawable/divider.png",使用图片资源的话,就不用dividerHeight这个了;

或是,android:divider="@null",android:dividerHeight="0dp",也就是说不使用系统的divider,我们在item中自己在底部添加:

listview_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/list_selector_blue"
    android:gravity="center_horizontal"
    android:orientation="vertical" >

    <!-- 最底部,content内容省略 -->
    <ImageView
        android:id="@+id/tool_list_item_divider"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginLeft="8dp"
        android:background="@drawable/divider_line_01"
        android:contentDescription="@null"
        android:visibility="visible" />

</LinearLayout>
这种方式的好处是divider可随意调整左右的距离,但是不好的地方是,listview最后一个item也会显示出来该divider,想要灵活,还得在代码中动态判断,效果如下:


【3】点击Item时,背景的变化,同样有两种方式,

直接使用listview自带的listSelector属性:android:listSelector="@drawable/list_selector_blue"

或是自定义,在item的xml中,最外层的Layout添加background属性,想要图片,透明,各种颜色都可以随意选择:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/list_selector_blue"
    android:gravity="center_horizontal"
    android:orientation="vertical" >
</LinearLayout>
【4】listview的上边和下边有黑色的阴影

解决办法:给listview加上android:fadingEdge=”none”属性;

【5】listview在拖动的时候背景图片消失变成黑色背景。等到拖动完毕我们自己的背景图片才显示出来
解决办法:给listview加上android:scrollingCache=”false”属性;但是如果你没有给listview使用复杂的背景图片,为了性能考虑,scrollingCache还是要设置为true;

【6】不显示scrollBar

android:scrollbars="none"

【7】drawSelectorOnTop属性

android:drawSelectorOnTop="true" 点击某一条记录,颜色会显示在最上面,记录上的文字被遮住,所以点击文字不放,文字就看不到
android:drawSelectorOnTop="false"点击某条记录不放,颜色会在记录的后面,成为背景色,但是记录内容的文字是可见的


android中state_checked和state_selected无法共存的问题


如上图,谁在前,谁的能有效,后者便会失效,应该是Checked事件和selected互斥的原因,建议这两者不要同时存在,使用一个即可。


android判断手机SIM卡运营商

	public String getSimInfo() {

		TelephonyManager telManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
		String operator = telManager.getSimOperator();
		String networkOperator = "";
		if (operator != null) {
			if (operator.equals("46000") || operator.equals("46002")
					|| operator.equals("46007")) {
				networkOperator = "中国移动";
			} else if (operator.equals("46001")) {
				networkOperator = "中国联通";
			} else if (operator.equals("46003")) {
				networkOperator = "中国电信";
			}
		}
		return networkOperator;
	}

多线程获取验证码(图片加载器)

直接上代码了:

package com.vixtel.netvista.sxcmcc.common;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

/**
 * @author yangxiaolong
 * @2015-3-10
 */
public class ImageLoader implements Runnable {

	/** 回调函数,传回一个Bitmap对象 */
	public interface onLoadListener {
		void onLoadFinished(Bitmap bitmap);
	};

	private static final String TAG = ImageLoader.class.getSimpleName();

	/** 回调监听 */
	protected onLoadListener onloadListener;

	/** 目标地址 */
	private String requestUrl;

	/** 打印LOG标记 */
	private boolean isDebug = true;

	public void setOnloadListener(onLoadListener onloadListener) {
		this.onloadListener = onloadListener;
	}

	public ImageLoader(String url) {
		requestUrl = url;
	}

	public void startLoad() {
		new Thread(this).start();
	}

	@Override
	public void run() {
		// requestByHttpClient();
		requestByHttpURLConnection();
	}

	/**
	 * 以HttpClient方式下载数据
	 */
	protected void requestByHttpClient() {
		HttpClient client = new DefaultHttpClient();

		client.getParams().setParameter(
				CoreConnectionPNames.CONNECTION_TIMEOUT, 30000);
		client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 30000);

		URI uri;
		InputStream is = null;
		BufferedInputStream bis = null;
		Bitmap bitmap = null;
		try {
			uri = new URI(requestUrl);
			debug("httpGet requestUrl:" + requestUrl + "  sessionId:"
					+ NetClientSession.sessionId);
			HttpGet get = new HttpGet(uri);
			// 如果需要sessionid的话,传入
			get.setHeader("Cookie", "nts-session-id="
					+ NetClientSession.sessionId);
			HttpResponse response = client.execute(get);
			debug("http response statusLine:" + response.getStatusLine());
			debug("http response entity:" + response.getEntity().toString());
			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				is = response.getEntity().getContent();
				bis = new BufferedInputStream(is);
				bitmap = BitmapFactory.decodeStream(bis);
				// 裁剪尺寸
				// Bitmap.createScaledBitmap(bitmap, dstWidth, dstHeight,
				// filter);
			}

			if (onloadListener != null) {
				onloadListener.onLoadFinished(bitmap);
			}

		} catch (URISyntaxException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			client.getConnectionManager().shutdown();
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				is = null;
			}
			if (bis != null) {
				try {
					bis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				bis = null;
			}
		}

	}

	/**
	 * 以HttpURLConnection方式下载数据
	 */
	protected void requestByHttpURLConnection() {

		URL url = null;

		HttpURLConnection conn = null;
		BufferedInputStream bis = null;
		Bitmap bitmap = null;
		try {
			url = new URL(requestUrl);
			debug("HttpURLConnection requestUrl:" + requestUrl + "  sessionId:"
					+ NetClientSession.sessionId);
			conn = (HttpURLConnection) url.openConnection();
			conn.setReadTimeout(30000);
			conn.setConnectTimeout(30000);
			conn.setRequestMethod("GET");
			// HttpURLConnection方式添加cookie等参数【如果需要cookie参数的话】
			conn.addRequestProperty("Cookie", "nts-session-id="
					+ NetClientSession.sessionId);
			if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
				bis = new BufferedInputStream(conn.getInputStream());

				// 方法一
				// bitmap = BitmapFactory.decodeStream(bis);

				// 方法二
				byte[] buffer = new byte[1024];
				int len = 0;
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				while ((len = bis.read(buffer)) != -1) {
					bos.write(buffer, 0, len);
				}
				byte[] data = bos.toByteArray();
				bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
			}

			if (onloadListener != null) {
				onloadListener.onLoadFinished(bitmap);
			}

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				conn.disconnect();
				conn = null;
			}
			if (bis != null) {
				try {
					bis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				bis = null;
			}
		}

	}

	private void debug(String msg) {
		if (isDebug) {
			Log.d(TAG, msg);
		}
	}

}

在跳转到相应界面时:

	ImageLoader imageloader = new ImageLoader(
				"http://XXXXXXXXX/getVerifyCodeImage?&width=95&height=21");
		imageloader.setOnloadListener(this);
		imageloader.startLoad();

在回调接口里:

	@Override
	public void onLoadFinished(final Bitmap bitmap) {
		if (bitmap != null) {
			mHandler.post(new Runnable() {

				@Override
				public void run() {
					verifyCodeView.setImageDrawable(new BitmapDrawable(bitmap));
				}
			});
		}
	}

效果图:



解决真机调试时Eclipse DDMS上打不开/data等目录的问题

比如我们在项目中使用了SQLite存储技术,然后想把保存的db文件,导入到电脑中,在Eclipse的File Explorer寻找data/data/应用名称/db 中便可以做到这个,不过首先是需要满足一个条件:手机ROOT

首先root:现在root简单多了,不像以前必须要通过破解程序强行刷入,现在可以利用诸如ROOT大师,一键获取ROOT便可以搞定了,这种样子类似于假的root,并不是深度,相比起来会安全很多。

其次,下载一个类似于RE管理器这样的软件,可以在手机上查看data、system等目录:


打开后,应该就会弹出root权限申请,同意后,顶部调成:挂载为可读写,这样的话,我们就能读写所有的文件夹了,现在手机打开data,肯定没问题吧,但是我们切换到Eclipse的DDMS下,File Explorer,还是打不开data,为什么?还有一步,如下图:

长按data文件夹,权限设定,其他组也勾选上读和写-同时应用到所有子文件和子文件夹,确定

接下来,再在DDMS下,打开data,试一试:

搞定


java中SimpleDateFormat 和android中DateFormat对日期的格式化差异

首先我们看SimpleDateFormat 的实现:

		SimpleDateFormat simpledateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
		String now = simpledateformat.format(new Date());
HH代表24小时制,hh代表12小时制

然后我们看下android版的DateFormat

				long time = 1427277590213L;
				String now = DateFormat.format("yyyy-MM-dd kk:mm:ss", time).toString();
注意到了吧,小时如果用HH会出问题,在某些手机上识别不了,会直接显示HH,所以只能用hh,这个代表了12小时制,如果我们用此API想实现24小时制的,就得用kk,然后就没问题了。

ListView 添加FooterView相关问题汇总

【footerview添加后看不到】,请记得,一定要在listview.setAdapter()之前addFooterView,否则无效的。

【HeaderView和FooterView不可选择或点击】,我们的headerView和footerView添加到listview后其实是相当于一个item,所以系统会自动加上点击和选中的效果,如果想禁用的话,使用这个方法:

public void addFooterView(View v, Object data, boolean isSelectable) 
public void addHeaderView(View v, Object data, boolean isSelectable)
如果在view里已经定义好相关数据,第二个参数就是null,第三个参数设为false,即不可选择

【HeaderView和FooterView动态添加和删除】,直接listview.removeFooterView()感觉太过暴力,如果footerview设置View visible为Gone的话,虽然内容不显示了,但是这块区域还是会被占用(显示),有一个不是办法的办法,如下:

需要隐藏时:

footerView.setVisibility(View.GONE);
footerView.setPadding(0, -footerLayout.getHeight(), 0, 0);
需要显示时:

footerView.setVisibility(View.VISIBLE);
footerView.setPadding(0, 0, 0, 0);

【使用ListView的footerView实现上拉加载更多的效果】

load_more.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dp" >

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true" >

        <ProgressBar
            android:id="@+id/loadmore_progressbar"
            style="@style/Progress_circle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginRight="2dp"
            android:visibility="visible" />

        <TextView
            android:id="@+id/loadmore_description"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="2dp"
            android:layout_toRightOf="@+id/loadmore_progressbar"
            android:text="加载中..."
            android:textColor="#999999"
            android:textSize="14sp" />
    </RelativeLayout>

</RelativeLayout>

java

		loadMoreView = getLayoutInflater().inflate(
				R.layout.listview_footview_loadmore, null, true);
		//before listview.setAdapter
		 mListView.addFooterView(loadMoreView, null, false);
实现OnScrollListener

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {

		if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
				&& lastItemIndex == recordAdapter.getCount()) {
			System.out.println("onScrollStateChanged:" + scrollState
					+ "   count:" + recordAdapter.getCount());
			// 假如总共有50条记录
			if (recordAdapter.getCount() >= 50) {
				mListView.removeFooterView(loadMoreView);
				Toast.makeText(this, "加载完毕", Toast.LENGTH_SHORT).show();
			} else {

				for (int i = 0; i < 5; i++) {
					insertData();
				}
				recordList = recordManager.findAll();
				recordAdapter.notifyDataSetChanged();

			}

		}

	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {

		// 第一个可见的item+当前界面所有可视的item总数减去1
		lastItemIndex = firstVisibleItem + visibleItemCount - 1;

	}
效果图



正确获取手机屏幕屋里尺寸,密度,密度DPI

在一个低密度的小屏手机上,仅靠上面的代码是不能获取正确的尺寸的。比如说,一部240x320像素的低密度手机,如果运行上述代码,获取到的屏幕尺寸是320x427。因此,研究之后发现,若没有设定多分辨率支持的话,Android系统会将240x320的低密度(120)尺寸转换为中等密度(160)对应的尺寸,这样的话就影响的APP的屏幕适配。所以,需要在工程的AndroidManifest.xml文件中,加入supports-screens节点,具体的内容如下:

    <supports-screens
        android:anyDensity="true"
        android:largeScreens="true"
        android:normalScreens="true"
        android:resizeable="true"
        android:smallScreens="true"
        android:xlargeScreens="true" />
这样的话,当前的Android程序就支持了多种分辨率,那么就可以得到正确的物理尺寸了:

		DisplayMetrics dm = new DisplayMetrics();
		// 取得窗口属性
		getWindowManager().getDefaultDisplay().getMetrics(dm);
		// 窗口的宽度
		int screenWidth = dm.widthPixels;// 屏幕宽度(像素)
		// 窗口高度
		int screenHeight = dm.heightPixels;// 屏幕高度(像素)
		float density = dm.density;// 屏幕密度(0.75 / 1.0 / 1.5)
		int densityDpi = dm.densityDpi;// 屏幕密度DPI(120 / 160 / 240)


#NDK开发# 遇到Eclipse报错“Unresolved inclusion jni.h”的解决办法

在配置NDK开发环境的适合,当JDK,Eclipse,Android SDK,一切就绪时,我们创建了一个Demo,并在项目添加了 Native Support支持,发现在编辑xx.cpp的时候,由于include的头文件都无法加载(或识别),导致代码内容symbol都出现错误,解决办法很简单。

        我们只在Eclipse-Peference-Android-JDK,添加了JDK的路径,其实光这么做是不够的,还需要添加一个JDK的PATH,配置一下bash_profile 打开Terminal

输入命令 pico .bash_profile 
首先添加一行 export PATH=${PATH}:/Users/admster/android/android-ndk-r10e
然后再来一行 A_NDK_ROOT=/Users/admaster/android/android-ndk-r10e
最后来一行  export A_NDK_ROOT


如上图所示,control+x 输入y 保存一下,enter,关闭终端,如果这个时候你还开启着Eclipse,关闭,重新打开,是不是cpp的错误解决了?


当然你也可以不去添加全局的环境变量,右键项目-properties-C/C++ Build-Environment,从这里添加环境变量,指向你的android-ndk项目路径。

如何环境变量ok,发现还是无法识别symbol和jni,终极大法,剔除当前工程的ndk相关配置,重新添加 Native support,方法如下:

1,关闭Eclipse或是close project,到工程目录(显示隐藏文件),打开.project文件,要删除的部分如下所示(红色代码),千万别都删了,不然无法识别Android工程:

<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>MMA_Oschina_SDK</name>
<comment></comment>
<projects>
</projects>
<buildSpec>

<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
<dictionary>
<key>?children?</key>
<value>?children?=?name?=entry\\\\\\\|\\\|?name?=entry\\\\\\\|\\\|\|?name?=outputEntries\||</value>
</dictionary>
<dictionary>
<key>?name?</key>
<value></value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.append_environment</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildArguments</key>
<value></value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildCommand</key>
<value>ndk-build</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
<value>clean</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.contents</key>
<value>org.eclipse.cdt.make.core.activeConfigSettings</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
<value>false</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableFullBuild</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.stopOnError</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
<value>true</value>
</dictionary>
</arguments>
</buildCommand>

<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>

<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>

<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>full,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/NDK_Builder.launch</value>
</dictionary>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>

<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>

</natures>
</projectDescription>

保存后,在当前目录寻找.cproject文件,删除之,现在可以重新open project了,记得刷新一下,此时邮件工程,Android Tools 应该可以重新显示:Add Native Support了,重新添加即可,此时应该可以识别symbol和jni等头文件了~

#NDK开发# 中的Method 'NewStringUTF' could not be resolved问题

在JNI开发时,我们编辑c或cpp文件,添加一个method时,有时候会提示这样的错误,但是好像明明已经导入了相关类的头文件,解决办法如下:

选中项目右键-properties,左侧tab列表选中C/C++ General-Code Analysis,右侧内容区域选择:Use Project settings,寻找到Method cannot be resolved一行,取消勾选,apply,保存,这样再编辑代码时,便不会提示这样的警告和错误了。


这只是设置在当前项目下,如果其他工程有遇到,同样的方式。http://www.oschina.net/question/95475_89610

#NDK开发# 使用javah,生成.h头文件时出现无法访问android.app,Activity的错误的解决

在jdk6时,是在bin/classes目录下执行命令,生成.h文件;在jdk7及以上时,是在src目录下执行命令,生成.h文件,具体如下图:



#NDK开发# java.lang.UnsatisfiedLinkError: findLibrary returned null

目标的.c或.cpp文件最好不要以lib为开头,这样自动生成的so文件是以libXXX.so为命名规则,在代码中调用:

	public native String getReply();

	static {
		System.loadLibrary("hellojni");
		Log.d("JNIDemo", "so success load~~~");
	}

hellojni时,会自动寻找项目libs/armeabi中的libhellojni.so,如果我们的c/cpp文件是以lib开头的,那么生成的so应该是liblibXXX.so,如果单独是一个lib开头,就会出现这样的错误,为了不引起歧义,自己的cpp文件还是不要加lib了,就用系统自己加的为准。

其实引起这个错误的问题有多个方面,不仅仅是我说的这个,放个传送门大家自己看:http://stackoverflow.com/questions/9037966/android-ndk-java-lang-unsatisfiedlinkerror-findlibrary-returned-null


#NDK开发# jni/hellocpp/main.cpp:16:18: error: base operand of '->' has non-pointer type 'JNIEnv {aka _JNIEnv}'

    这个错误一般发生在我们进行NDK(JNI)开发时的C/C++文件中,一眼望去是识别不了函数或是变量type,其实是C和C++对于这个返回函数:NewStringUTF的不同使用方法~

代码的写法是:

jstring  Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz, jstring str, jint i ){
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}
错误在于:

(*env)->NewStringUTF(env, "Hello from JNI !"); 这一行,这是c的写法

而cpp的话,需要改写成: env->NewStringUTF( "Hello from JNI !");


#NDK开发# Native method not found错误以及如何生成一个公用的SO库

出现Native method not found这个错误有很多因素,最常见的就是C/C++函数声明时大小写(Java)或是路径不对,导致SO调用时找不到相应的类

JNIEXPORT jstring JNICALL Java_com_admaster_jnitest_AppMain_getReply
  (JNIEnv *, jobject);

其次,如果使用C++代码,别忘了在cpp声明:

extern "C" {
//..
}

放上传送门一个:http://blog.csdn.net/liranke/article/details/41078137

其实想说的是,我的Project01,添加了Native Support支持,并且进行了NDK开发,build run在libs/armvabi下生成了.so文件(比如方法路径是:com_ndk_test_AppMain_Method1),但是我拷贝到Project02的libs下,同时在需要使用功能的地方加载了so库,使用某一个功能(method)时,出现了native method not found的错误,这是很正常的,在project02下,是完全不同的package,它是找不到com_ndk_test_AppMain_Method1这个路径下的函数的~

所以,想做一个可以共同使用的so库,就需要另一个项目,创建一个以so里方法命名的package和类名,在project02创建一个:com.ndk.test的package,创建一个AppMain的类文件,在此loadLibrary和声明 Native Method。

像baidu地图,都是so库结合SDK一起使用的,在它自己的SDK里,加载SO,然后提供公共接口和功能供用户使用。


#NDK开发# 如何添加LOG调试以及出现unresolved ANDROID_LOG_INFO 和undefined reference to `__android_log_print' 错误

在NDK中添加LOG函数,并且可以在logcat中输出的办法:

第一步 在.h或是.c或是.cpp中头部添加:

#include <android/log.h>

#define  LOG_TAG    "ctool"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

__android_log_print(ANDROID_LOG_INFO,LOG_TAG,TITLE)
第一个参数ANDROID_LOG_INFO(还有ANDROID_LOG_ERROR等),表示什么类型的输出,上面的函数相当于android的java代码的Log.i(LOG_TAG,TITLE),第二个参数就是logcat里的tag,第三个就是打印的内容,再详细可以log.h中查看。

__android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,“***”) // LOG类型:debug
  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,“***”) // LOG类型:info
 __android_log_print(ANDROID_LOG_WARN,LOG_TAG,“***”) // LOG类型:warning
 __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,“***”) // LOG类型:error
  __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,“***”) // LOG类型:Verbose

第二步 在Android.MK文件中添加:

LOCAL_LDLIBS += -llog

如果是用BUILD_SHARED_LIBRARY生成.so,那么在Android.apk中添加下面语句
LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog(注:若生成static的.a,只需添加 LOCAL_LDLIBS:=-llog )


#NDK开发# 两种方式build生成so文件,以及同时生成支持多架构的so动态库(armeabi,x86...)

首选当工程配置一切都ok后,build应用有两种方式:

Eclipse(模式切换到C/C++):


命令行:

Terminals定位到workspace工程目录,直接运行ndk-build命令即可(前提你要添加了ndk的环境变量)

现在说一下如何同时生成armeabi,x86,x86_64等不同架构的so库,其实只需要一个配置即可

必备条件:ndk10e以上才支持

在jni目录下创建一个Application.mk文件,注意,不是Android.mk,添加如下:

APP_ABI := armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mips64

想支持的架构就添加上去,以空格为分隔符,官方文档:http://developer.android.com/ndk/guides/abis.html






签名打包时proguard returned with error code 1混淆的错误:jpush [cn/jpush/android/a/a.<clinit>()V]) 

在console上看一下是不是极光推送那边出的问题,如果是的话,很大几率是你的Proguard版本过低,下载4.x以上的proguard.jar 然后替换到sdk的proguard目录下即可。


'<>' operator is not allowed for source level below 1.7

这个错误基本上出现于导入项目到Eclipse时,原因是当前该项目在Eclipse下的JRE环境与项目要求不一致,解决办法如下:

右键项目-属性-Java-Compiler,在右侧:Compiler Compliance level调整到1.7,即可。


TextView在xml里配置onClick事件不起作用的问题

android:clickable="true"

android:onClick="titleAction"

一定要记得加上clickable为true,默认为false,我们又不在代码中setOnclickListener,所以就无法响应事件咯,其它可以在xml添加onClick的组件也是同样道理。


handler中obtainmessge与new message区别

		//从线程池中取一个可循环利用的Message对象
		Message msg  = Message.obtain();
		msg.what = 1;
		//Handler 的obtain,最终会调用到Message.obtain()
		Message msg = mHandler.obtainMessage(1);
		//最终会走到handler.sendMessage()
		msg.sendToTarget();
		
		//new
		Message msg = new Message();
		
		mHandler.sendMessage(msg);

简单来讲,obtain从名字就可以知道,从线程池里获取一个可以循环利用的Message对象,而new Message(),将会开辟了新的内存空间生成Message对象,所以常规情况下,尽量使用obtain方式获取Message对象,以节省开销。


在不同View下取相同组件id(名称)在R文件只会显示一个

在active1.xml中定义一个id为 title_tv 的TextView,然后在active2.xml中定义同样id的TextView,但在.R文件中,我们只能看到一个id值,但在实际打印中,其实是会生成各自的实例,如下图:

public static final class id {public static final int title_tv=0x7f08001c;}

  tv:2131230748   android.widget.TextView{42d97b50 V.ED..C. ......I. 0,0-0,0 #7f08001c app:id/title_tv}

  tv:2131230748   android.widget.TextView{42efd7c0 V.ED..C. ......I. 0,0-0,0 #7f08001c app:id/title_tv}


在xml文件里@+id/和@id/的区别

@ 可以理解为引用,而+代表自己新声明的,即:@id代表引用已有的id,而@+id是新增加一个id。

Android中的组件需要用一个int类型的值来表示,这个值也就是组件标签中的id 属性值。id属性只能接受资源类型的值,也就是必须以@开头的值,例如,@id/btn 、@+id/textview等。

如果在@后面使用“+”,表示当修改完某个布局文件并保存后,系统会自动在R.java文件中生成相应的int类型变量。变量名就是“/”后面的值,例如,@+id/my_btn会在R.java文件中生成int my_btn = value,其中value是一个十六进制的数。如果my_btn在R.java中已经存在同名的变量,就不再生成新的变量,而该组件会使用这个已存在的变量的值。 

使用@+id/name形式,当R.java中存在名为name变量时,则该组件会使用该变量的值作为标识。如果不存在该变量,则添加一个新的变量,并为该变量赋相应的值(不会重复)。  

而@id/name的形式,相当于引用已经存在的id值,比如@id/android:list,代表引用android系统的R类所在的package的名为list的id。


LayoutInflate中的 inflate 两个构造参数的区别

1. public View inflate(int resource, ViewGroup root)  //attachToRoot在内部实现默认为true
2. public View inflate(int resource, ViewGroup root, boolean attachToRoot) //attachToRoot默认为false

第一个参数指出要载入的布局文件资源,第二个参数指出视图结构中载入的布局将要放入的根视图。如果有第三个参数,那么它用来决定是否把载入后的视图绑定到给出的根视图中。
最后两个参数可能会导致一些问题。如果使用两个参数的版本,Layoutinflater会自动尝试把载入的视图绑定到给定的根视图对象中。但是,如果你传递null,系统就不会尝试绑定操作了。(因为它根本不知道跟视图是谁)

不知你有没有注意到这一点,每次Framework需要你去载入一个布局文件时,都会传入一个ViewGroup参数(最后需要绑定到的根视图),如果Layoutinflater设为自动绑定到根视图的话,会抛出一个异常。
所以你想想看,如果我做绑定操作的话,为什么要给你一个ViewGroup参数呢?事实证明父视图在这个inflation操作过程中是很重要的,它会计算被载入的XML在根元素中的LayoutParams,如果传入null话,就等于是告诉框架“我不知道载入的View要放到哪个父视图中”。
问题在于,android:layout_xxx属性会在父视图对象中被重新计算,结果就是所有你定义的LayoutParams都会被忽略掉(因为没有已知的父视图对象)。然后你就纳闷“为什么框架会忽略掉我自己定义的布局属性呢?还是去StackOverFlow上看看,提一个bug吧”。
如果没有设置LayoutParams,那么最终ViewGroup也会给你生成一个默认的属性,幸运的话(很多时候),这些默认的设置正好和你在XML文件中定义的一样……所以你就察觉不到其实已经出现问题了。

实际试验下,为ListView简单的载入一个布局文件:R.layout.Item_row.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:gravity="center_vertical"
    android:orientation="horizontal">
    <TextView
        android:id="@+id/text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingRight="15dp"
        android:text="Text1" />
    <TextView
        android:id="@+id/text2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="Text2" />
</LinearLayout>
我们想把每一个item的高度设置成60dp,在Adapter的getView加载下:

public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = inflate(R.layout.item_row, null);
    }
  
    return convertView;
}
看下结果:

为什么设定的固定高度不起作用?而如果这样载入布局的话就没有问题:

public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = inflate(R.layout.item_row, parent, false);
    }
  
    return convertView;
}

这就得到了我们想要的结果:

看下源码关键实现的地方:

         if (root != null) {  
                        if (DEBUG) {  
                            System.out.println("Creating params from root: " +  
                                    root);  
                        }  
                        // Create layout params that match root, if supplied  
                        params = root.generateLayoutParams(attrs);  
                        if (!attachToRoot) {  
                            // Set the layout params for temp if we are not  
                            // attaching. (If we are, we use addView, below)  
                            temp.setLayoutParams(params);  
                        }  
                    }  
还有

   // We are supposed to attach all the views we found (int temp)  
                    // to root. Do that now.  
                    if (root != null && attachToRoot) {  
                        root.addView(temp, params);  
                    }  
  
                    // Decide whether to return the root that was passed in or the  
                    // top view found in xml.  
                    if (root == null || !attachToRoot) {  
                        result = temp;  
                    }  
当我们传进来的root不是null,并且第三个参数是false的时候,这个temp就被加入到了root中,并且把root当作最终的返回值返回了。而当我们设置root为空的时候,没有设置LayoutParams参数的temp对象,作为返回值返回了。

参考:http://blog.jobbole.com/72156/

调试build时可成功运行,但签名打包时报错:class找不到

这种情况多半是由于工程庞大,嵌入了很多的内部或是外部的class,我遇到的是自定义的Application找不到,起初clean下重新Export Sign Application即可,后来也不太好使,看来不一定是概率问题。综合网友反馈,大多数是这两个问题:

1,混淆不规范或是错误导致,建议带着报错(classNoFound)的class去查看proguard.cfg。

2,如果混淆配置从来没改过,可以尝试如下操作:

Project -> Build Automatically,即取消Build Automatically.关闭Eclipse自动编译
Project -> Clean
Project -> Build
Android Tools -> Export Signed application


调试错误 INSTALL_FAILED_NO_MATCHING_ABIS

基本出现在模拟器调试时,大多数原因是由于使用了native libraries 。但 libraries 不支持当前的cpu的架构(x86,armabi等),修改方式就是最好生成支持该架构的so库,如果是Genymotaion模拟器出现的错误,我们可以下载一个支持的插件,将下载好的zip包用鼠标拖到虚拟机窗口中,出现确认对跨框点OK就行。然后重启即可。


targetSdkVersion和project.properties中target属性的区别和联系

AndroidMenifest.xml中我们一般会声明:

    <uses-sdk
        android:minSdkVersion="14"
        android:maxSdkVersion="23"
        android:targetSdkVersion="23" />
同时,在Eclipse Ant构建的Project工程目录的 project.properties 中,也会看到语句: target=android-17,简单说一下他们的区别和联系

minSdkVersionmaxSdkVersion比较容易理解,就是在安装程序的时候,如果目标设备的API版本小于minSdkVersion, 或者大于maxSdkVersion,程序将无法安装。一般来说没有必要设置maxSdkVersion。

  targetSdkVersion相对复杂一些,如果设置了此属性,那么在程序执行时,如果目标设备的API版本正好等于此数值, 他会告诉Android系统:此程序在此版本已经经过充分测,没有问题,不必为此程序开启兼容性检查判断的工作了。 也就是说,如果targetSdkVersion与目标设备的API版本相同时,运行效率可能会高一些。 

但是,这个设置仅仅是一个声明,不会有太实质的作用, 比如说,使用了targetSdkVersion(比如4.4.2)SDK版本中的一个特性,但是这个特性在低版本中是不支持的 ,那么在低版本(2.3.3)的API(SDK)设备上运行程序时,可能会报错:java.lang.VerifyError。也就是说,此属性不会帮你解决兼容性的测试问题。( 发布程序前至少需要在minSdkVersion版本的设备上将程序完整的跑一遍来确定兼容性问题)

roject.properties中的target是指在编译的时候使用哪个版本的API进行编译(使用低版本sdk编译,但是代码中用到了高版本的api,便会报错)。

综上,上面的四个值其实是作用于不同的时期:
target API level是在编译的时候起作用,用于指定使用哪个API版本(SDK版本)进行编译。 

minSdkVersion和maxSdkVersion是在程序安装的时候起作用, 用于指定哪些版本的设备可以安装此应用。

targetSdkVersion是在程序运行的时候起作用,用于提高指定版本的设备上程序运行体验。


通过实际测试发现,targetSdkVersion还是要慎重设置,比如当前我在一款Android6.0的手机系统下运行一个demo,demo的targetSdkVersion=23,和当前设备一致,不会进行兼容性检查的效果如下:


默认权限系统都不会赋予,并且都是关闭着的(这个功能只有在6.0+的系统下会提供,低于此版本可以无视),需要用户主动开启,这所带来的问题便是我在demo中获取了设备的imei,这需要用到android.permission.READ_PHONE_STATE权限,在targetSdkVersion=6.0的情况下,权限默认不赋予,当使用需要某个权限的API时,如果没有trycatch,便会直接crash。

	TelephonyManager manager = (TelephonyManager) context
				.getSystemService(Context.TELEPHONY_SERVICE);
		if (manager != null) {//在6.0+手机上,不赋予该权限,manager是不为null的,直到getDeviceId()便会崩溃,提示无权限
			return manager.getDeviceId();
		}
废了这么大口舌,这个小插曲主要说明在适配6.0系统的权限管理时,一定要在工程所有需要在manifest中声明的权限所用到的API添加trycatch和权限检查,以免用户之后手动关闭权限。






  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值