出现本错误的一般有两种情况
第一种情况:导包错误--检查import,找到这个:
删除之,再重新导入含有包名的R文件。
第二种情况:本情况应该更为多见,一般为布局文件中有错误,而无法生存R文件,可以检查一下:
你会发现果真没有生成R文件,这时你需要解决的就是查找布局文件中的错误,改正错误,生成R文件之后,本错误就会消失啦!
fragment中添加了button和checkbox这些控件,此时这些子控件会将焦点获取到,所以常常当点击item时变化的是子控件,item本身的点击没有响应。
这时候就可以使用descendantFocusability来解决啦,API描述如下:
android:descendantFocusability
该属性是当一个为view获取焦点时,定义viewGroup和其子控件两者之间的关系。
属性的值有三种:
beforeDescendants:viewgroup会优先其子类控件而获取到焦点
afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点
通常我们用到的是第三种,即在Item布局的根布局加上android:descendantFocusability=”blocksDescendants”的属性。
写这个demo顺便复习一下BaseAdapter
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class Audition1 extends Activity {
private ListView listView;
a
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audition1);
initComponents();
}
private void initComponents() {
listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(new MyAdapter(this));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
Toast.makeText(getApplicationContext(), "item", 300).show();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_audition1, menu);
return true;
}
public final class ViewHolder {
public TextView textView;
public Button button;
public ImageView imageView;
}
class MyAdapter extends BaseAdapter {
private LayoutInflater mInflater;
public MyAdapter(Context context) {
this.mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return 3;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.item, null);
holder.imageView = (ImageView) convertView
.findViewById(R.id.imageView);
holder.textView = (TextView) convertView
.findViewById(R.id.textViewId);
holder.button = (Button) convertView.findViewById(R.id.button);
holder.textView.setText("shit");
holder.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(
Audition1.this);
builder.setMessage("dialog");
builder.setTitle("title");
builder.create();
builder.show();
}
});
convertView.setTag(holder);
} else {
convertView.getTag();
}
return convertView;
}
}
}
<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" >
<ListView
android:id="@+id/listView"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:padding="@dimen/padding_medium"
tools:context=".Audition1"
android:dividerHeight="5dp"/>
</RelativeLayout>?
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/textViewId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
/>
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher"
android:contentDescription="@string/app_name"
/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="press"
android:focusable="false"
/>
</LinearLayout>
原因是button强制获取了item的焦点,只要设置button的focusable为false即可。
findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。Android中FindViewById()是一个非常常用的函数,位于android.app.Activity包中。该函数利用我们在XML文件中定义的View的id属性来获取相应的View对象。findViewById()属于API Level 1, 对应的android版本是android1.0, 由此,可以看出,该函数是android早期版本中就有的。顺便说一下, android目前市场上已商用的版本及其对应的API Level如下:
android 1.0API Level 1
android 1.1API Level 2
android 1.5API Level 3
android 1.6API Level 4
android 2.0API Level 5
android 2.0.1 API Level 6
android 2.1API Level 7
android 2.2API Level 8
1、参数错误:findViewById的参数是一个View的ID,如果在XML文件中没定义相应的ID,则程序会
善意的告诉你:XXX cannot be resulved。此时,补充定义就可以了。
2、未指定调用布局:findViewById()的调用与具体的布局有关,默认的是main.xml中的布局,函数前
没有布局指示。不过,当我们在main.xml描述的布局中,添加其它布局时,利用该函数获
取所添加布局中的View,则需在调用时,添加布局名,形式如下:
addLayout.findViewById(), 如果不这么做,程序编译时有时不会报错,但运行时会
提示遇到异常,并强制关闭应用。
3、命名冲突:这个错误可能不是很常见,不过,要是没有遇到过,猛的来这么一下,还真让人 有点懵。
呵呵,本人就犯过这样的错误。解释下,这里的命名冲突是指当前工程中定义的类与
android在Framework中提供的名字相同,这样的话,当前工程文件中,会优先使用本工
程中的定义。当然,使用findViewById()函数时,发生这种错误必须满足以下几个条件:
一是:需要在当前工程中利用ID(xml中定义)来查找对应的View对象;
二是:查找的View类名恰好与本工程中已有的类定义相同
三是:同名的两个类实例化后产生的对象类型不同,如:一个是View, 一个是Activity。
问题现象:
这样的代码熟悉吗?一个控件比较多的页面一直重复写这样的代码有没有很麻烦?
解决方法:
自定义一个方法:
public <T> T $(int viewID) {
return (T) findViewById(viewID);
}
然后不管是什么类型的View,直接一个$方法搞定:
Android 反射简化findViewById .
官方例子里的小玩意。。。。。
一个注解:InjectView
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Use this annotation to mark the fields of your Activity as being injectable.
* <p>
* See the {@link Injector} class for more details of how this operates.
*/
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
/**
* The resource id of the View to find and inject.
*/
public int value();
}
一个通过反射注解 并 实例化的功能类:
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import android.app.Activity;
/**
* Very lightweight form of injection, inspired by RoboGuice, for injecting common ui elements.
* <p>
* Usage is very simple. In your Activity, define some fields as follows:
*
* <pre class="code">
* @InjectView(R.id.fetch_button)
* private Button mFetchButton;
* @InjectView(R.id.submit_button)
* private Button mSubmitButton;
* @InjectView(R.id.main_view)
* private TextView mTextView;
* </pre>
* <p>
* Then, inside your Activity's onCreate() method, perform the injection like this:
*
* <pre class="code">
* setContentView(R.layout.main_layout);
* Injector.get(this).inject();
* </pre>
* <p>
* See the {@link #inject()} method for full details of how it works. Note that the fields are
* fetched and assigned at the time you call {@link #inject()}, consequently you should not do this
* until after you've called the setContentView() method.
*/
public final class Injector {
private final Activity mActivity;
private Injector(Activity activity) {
mActivity = activity;
}
/**
* Gets an {@link Injector} capable of injecting fields for the given Activity.
*/
public static Injector get(Activity activity) {
return new Injector(activity);
}
/**
* Injects all fields that are marked with the {@link InjectView} annotation.
* <p>
* For each field marked with the InjectView annotation, a call to
* {@link Activity#findViewById(int)} will be made, passing in the resource id stored in the
* value() method of the InjectView annotation as the int parameter, and the result of this call
* will be assigned to the field.
*
* @throws IllegalStateException if injection fails, common causes being that you have used an
* invalid id value, or you haven't called setContentView() on your Activity.
*/
public void inject() {
for (Field field : mActivity.getClass().getDeclaredFields()) {
for (Annotation annotation : field.getAnnotations()) {
if (annotation.annotationType().equals(InjectView.class)) {
try {
Class<?> fieldType = field.getType();
int idValue = InjectView.class.cast(annotation).value();
field.setAccessible(true);
Object injectedValue = fieldType.cast(mActivity.findViewById(idValue));
if (injectedValue == null) {
throw new IllegalStateException("findViewById(" + idValue
+ ") gave null for " +
field + ", can't inject");
}
field.set(mActivity, injectedValue);
field.setAccessible(false);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
}
}
}
使用时类似:
01.@InjectView(R.id.gygallery)
02.private Gallery gallery;
03.@InjectView(R.id.is_switcher)
04.private ImageSwitcher imageSwitcher;
@InjectView(R.id.gygallery)
private Gallery gallery;
@InjectView(R.id.is_switcher)
private ImageSwitcher imageSwitcher;
Activity>onCreate(){
Injector.get(this).inject();//init views
}
本文我在网上总结了几篇关于禁止Android横竖屏,解决切换屏幕时重启Activity的方法,解决Android手机 屏幕横竖屏切换,如何让Android横竖屏切换时不销毁当前activity。禁止Android横竖屏和解决切换屏幕时重启Activity的方法
1.在AndroidManifest.xml的Activity配置中加入 android:screenOrientation=”landscape”属性(landscape是横向,portrait是纵向)。如:
<activity android:name=".ContactsManagerActivity" android:label="@string/app_name" android:screenorientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN"> <category android:name="android.intent.category.LAUNCHER"> </category></action></intent-filter> </activity>
2.一般横竖屏切换时,activity要重启,为了避免重启,可以为activity 添加android:configChanges=“orientation”属性,然后在activity中复写onConfigurationChanged()方法,例如:
public void onConfigurationChanged(Configuration newConfig) { // TODO Auto-generated method stub if (newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE) { setContentView(R.layout.imageswitch); //横屏 } else { setContentView(R.layout.editcontact);//竖屏 } super.onConfigurationChanged(newConfig); }
这样就可以实现不重启activity,实现横竖屏切换了。
解决Android手机 屏幕横竖屏切换
Android中当屏幕横竖屏切换时,Activity的生命周期是重新加载(说明当前的Activity给销毁了,但又重新执行加载),怎么使屏幕横竖屏切换时,当前的Activity不销毁呢?
1. 在AndroidManifest.xml中为Activity设置configChanges属性,
application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
configChanges有如下选项: 1. orientation :屏幕在纵向和横向间旋转, 2. keyboardHidden:键盘显示或隐藏 ,3.fontScale:用户变更了首选的字体大小 4.locale : 用户选择了不同的语言设定,5. keyboard :键盘类型变更,例如手机从12键盘切换到全键盘 6. touchscreen或navigation:键盘或导航方式变化,
如果缺少了keyboardHidden选项 不能防止Activity的销毁,并且在之后提到的onConfigurationChanged事件中 只能捕获竖屏变横屏的事件 不能捕获横屏变竖屏
2. 在对应的Activity中重写:onConfigurationChanged 方法:
public class MainActivity extends Activity { private TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.i("--Main--", "onCreate"); textView=(TextView)findViewById(R.id.tv_id); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.i("--Main--", "onConfigurationChanged"); if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){ textView.setText("当前屏幕为横屏"); }else{ textView.setText("当前屏幕为竖屏"); } } }
布局文件就是一个简单的TextView此处不给出,
效果如下:
日志打印:
从日志中可以分析出屏幕横竖屏切换时Activity并没有销毁,当然你也可以运行项目在onCreate方法打个断点,执行发现onCreate方法只是在刚开始进入时执行,屏幕横竖屏切换时,已经不会在执行,因此可在onConfigurationChanged方法中下点文章!
注:如果项目不需要屏幕切换时可以设置为
1. android:screenOrientation="portrait" 始终以竖屏显示
2. android:screenOrientation="landscape" 始终以横屏显示
上面的配置文件设置屏幕横竖屏,下面是代码去控制屏幕横竖屏的:
private OnClickListener onClick=new OnClickListener() { @Override public void onClick(View v) { //设置屏幕为横屏 if(v==butLandscrpe){ MainActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); //设置为置屏幕为竖屏 }else{ MainActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } } }; //监听系统设置的更改 @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); String message=newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE ? "屏幕设置为:横屏" : "屏幕设置为:竖屏"; showToast(message); }
横竖屏切换时候Activity的生命周期
1、新建一个Activity,并把各个生命周期打印出来
2、运行Activity,得到如下信息
onCreate-->onStart-->onResume-->
3、按crtl+f12切换成横屏时
onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->
4、再按crtl+f12切换成竖屏时,发现打印了两次相同的log
onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->
5、修改AndroidManifest.xml,把该Activity添加 android:configChanges="orientation",执行步骤3
onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->
6、再执行步骤4,发现不会再打印相同信息,但多打印了一行onConfigChanged
onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->onConfigurationChanged-->
7、把步骤5的android:configChanges="orientation" 改成 android:configChanges="orientation|keyboardHidden",执行步骤3,就只打印onConfigChanged
onConfigurationChanged-->
8、执行步骤4
onConfigurationChanged-->onConfigurationChanged-->
总结:
1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
3、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
总结一下整个Activity的生命周期
补充一点,当前Activity产生事件弹出Toast和AlertDialog的时候Activity的生命周期不会有改变
Activity运行时按下HOME键(跟被完全覆盖是一样的):onSaveInstanceState --> onPause --> onStop onRestart -->onStart--->onResume
Activity未被完全覆盖只是失去焦点:onPause--->onResume
如何让Android横竖屏切换时不销毁当前activity
我们都知道android 屏幕横向或竖向相互转换的时候会销毁当前的Activity 然后重启它。
为了横竖屏切换时不销毁当前Activity 我来介绍两种方法。
方法一:不允许横竖屏切换:
实现方法: Activity中还有一属性和屏幕方向有关:
android:screenOrientation=["unspecified" | "user" | "behind" | "landscape" | "portrait" | "sensor" | "nosensor"] 在Mainifest.xml的Activity元素中增加这么一个属性: android:screenOrientation=”portrait”则无论手机如何变动,拥有这个属性的activity都将是竖屏显示。 android:screenOrientation=”landscape”,为横屏显示。
//--------------------------------------------------------------------------------------------
方法二:
首先在Mainifest.xml的Activity元素中加入android:configChanges=”orientation|keyboardHidden”属性:
加入这条属性的含义是,应用程序将会处理屏幕方向和键盘状态(推出或合上)信息的改动。
但对于其他的设备配置信息的改动则会由Android系统来处理(销毁当前Activity,然后重启一个新的Activity实例)
然后 在java代码的activity子类中加入配置信息改动的处理代码:
//----------------------------------------------------------------------------------------- @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // 检测屏幕的方向:纵向或横向 if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { //当前为横屏, 在此处添加额外的处理代码 } else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { //当前为竖屏, 在此处添加额外的处理代码 } //检测实体键盘的状态:推出或者合上 if (newConfig.hardKeyboardHidden ) == Configuration.HARDKEYBOARDHIDDEN_NO) { //实体键盘处于推出状态,在此处添加额外的处理代码 } else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { //实体键盘处于合上状态,在此处添加额外的处理代码 } }