ANR, когда я нажимаю на элемент списка в listView
Я использую пользовательский listView и ArrayAdapter, который реализует sectionIndexer. Я использую Json для анализа данных с моего сервера. Я могу отобразить данные в listView, но когда я нажимаю на свой listView, он должен перейти к следующей подробной странице, но на данный момент он не работает.
Вот код
public class CustomListView extends ListView {
private Context ctx;
private static int indWidth = 20;
private String[] sections;
private float scaledWidth;
private float sx;
private int indexSize;
private String section;
private boolean showLetter = true;
private Handler listHandler;
public CustomListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
ctx = context;
}
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
ctx = context;
}
public CustomListView(Context context, String keyList) {
super(context);
ctx = context;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
scaledWidth = indWidth * getSizeInPixel(ctx);
sx = this.getWidth() - this.getPaddingRight() - scaledWidth;
Paint p = new Paint();
p.setColor(Color.WHITE);
p.setAlpha(100);
canvas.drawRect(sx, this.getPaddingTop(), sx + scaledWidth,
this.getHeight() - this.getPaddingBottom(), p);
indexSize = (this.getHeight() - this.getPaddingTop() - getPaddingBottom())
/ sections.length;
Paint textPaint = new Paint();
textPaint.setColor(Color.DKGRAY);
textPaint.setTextSize(scaledWidth / 2);
for (int i = 0; i < sections.length; i++)
canvas.drawText(sections[i].toUpperCase(),
sx + textPaint.getTextSize() / 2, getPaddingTop()
+ indexSize * (i + 1), textPaint);
// We draw the letter in the middle
if (showLetter & section != null && !section.equals("")) {
Paint textPaint2 = new Paint();
textPaint2.setColor(Color.DKGRAY);
textPaint2.setTextSize(2 * indWidth);
canvas.drawText(section.toUpperCase(), getWidth() / 2, getHeight() / 2, textPaint2);
}
}
private static float getSizeInPixel(Context ctx) {
return ctx.getResources().getDisplayMetrics().density;
}
@Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
if (adapter instanceof SectionIndexer)
sections = (String[]) ((SectionIndexer) adapter).getSections();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (x < sx)
return super.onTouchEvent(event);
else {
// We touched the index bar
float y = event.getY() - this.getPaddingTop() - getPaddingBottom();
int currentPosition = (int) Math.floor(y / indexSize);
section = sections[currentPosition];
this.setSelection(((SectionIndexer) getAdapter())
.getPositionForSection(currentPosition));
}
break;
}
case MotionEvent.ACTION_MOVE: {
if (x < sx)
return super.onTouchEvent(event);
else {
float y = event.getY();
int currentPosition = (int) Math.floor(y / indexSize);
section = sections[currentPosition];
this.setSelection(((SectionIndexer) getAdapter())
.getPositionForSection(currentPosition));
}
break;
}
case MotionEvent.ACTION_UP: {
listHandler = new ListHandler();
listHandler.sendEmptyMessageDelayed(0, 30 * 1000);
break;
}
}
return true;
}
private class ListHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
showLetter = false;
//CustomListView.this.invalidate();
}
}
}
Из этого класса я создаю просмотр списка в xml, активность
<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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<SearchView
android:id="@+id/searchView1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
</SearchView>
<com.deiontech.masjidtimetableapp.CustomListView
android:id="@+id/listViewformasjid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_below="@+id/searchView1"
android:clickable="true"/>
</RelativeLayout>
Это xml для каждой строки в listView
<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"
android:padding="3dp"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<TextView
android:id="@+id/textView_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:focusable="false"
android:text="t1" />
<TextView
android:id="@+id/textView_country"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView_name"
android:layout_below="@+id/textView_name"
android:layout_marginTop="19dp"
android:focusable="false"
android:text="t2" />
<TextView
android:id="@+id/textView_localarea"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView_country"
android:layout_below="@+id/textView_country"
android:layout_marginTop="15dp"
android:focusable="false"
android:text="t3" />
</RelativeLayout>
Это мой класс адаптера массива
public class SelectMasjidAdapter extends ArrayAdapter< MasjidForListClass> implements
SectionIndexer{
private List< MasjidForListClass> itemList;
private Context context;
private static String sections = "abcdefghilmnopqrstuvz";
ArrayList< MasjidForListClass> data;
public SelectMasjidAdapter(List< MasjidForListClass> itemList, Context ctx) {
super(ctx,R.layout.select_masjid_list_row, itemList);
this.itemList = itemList;
this.context = ctx;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return itemList.size();
}
@Override
public MasjidForListClass getItem(int position) {
// TODO Auto-generated method stub
return super.getItem(position);
}
@Override
public long getItemId(int arg0) {
return itemList.get(arg0).hashCode();
}
@Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View vi=arg1;
if(arg1==null)
vi = inflater.inflate(R.layout.select_masjid_list_row, null);
TextView masjid_name_text = (TextView)vi.findViewById(R.id.textView_name);
TextView masjid_country_text = (TextView)vi.findViewById(R.id.textView_country);
TextView masjid_localarea_text =
(TextView)vi.findViewById(R.id.textView_localarea);
MasjidForListClass m = itemList.get(arg0);
masjid_name_text.setText(m.getMasjidname());
masjid_country_text.setText(m.getMasjidcountry());
masjid_localarea_text.setText(m.getMasjidlocalarea());
return vi;
}
@Override
public int getPositionForSection(int sectionIndex) {
Log.d("ListView", "Get position for section");
for (int i=0; i < this.getCount(); i++) {
String item = this.getItem(i).getMasjidname().toLowerCase();
if (item.charAt(0) == sections.charAt(sectionIndex))
return i;
}
return 0;
}
@Override
public int getSectionForPosition(int position) {
Log.d("ListView", "Get section");
return 0;
}
@Override
public Object[] getSections() {
Log.d("ListView", "Get sections");
String[] sectionsArr = new String[sections.length()];
for (int i=0; i < sections.length(); i++)
sectionsArr[i] = "" + sections.charAt(i);
return sectionsArr;
}
}
И это мой класс деятельности
public class SelectMasjid extends Activity{
SelectMasjidAdapter adapter;
ListView masjidListView;
List <MasjidForListClass> masjidnames;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.select_masjid_activity);
GetJsonList l = new GetJsonList();
masjidnames = l.getlistOfMasjids();
Collections.sort(masjidnames, new Comparator<MasjidForListClass>() {
public int compare(MasjidForListClass result1, MasjidForListClass
result2) {
return result1.getMasjidname().compareTo(result2.getMasjidname());
}
});
masjidListView = (ListView)findViewById(R.id.listViewformasjid);
adapter = new SelectMasjidAdapter(masjidnames,this);
masjidListView.setAdapter(adapter);
masjidListView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
Intent i = new Intent(SelectMasjid.this,
MasjidDetailedActivity.class);
i.putParcelableArrayListExtra("selected", (ArrayList<?
extends Parcelable>) masjidnames);
i.putExtra("index", arg2);
startActivity(i);
}
});
}
}
Я не получаю никаких сведений об ошибке в logcat или приложение неожиданно не закрывается, поэтому я не могу решить эту проблему
Пожалуйста, помогите мне с этим, спасибо заранее.
3 ответа
ANR происходит потому, что вы выполняете длинную задачу в главном потоке SelectMasjid
деятельность. GetJsonList
код должен быть перемещен в асинхронной задаче.
Вот документация AsyncTask
Для настраиваемого listView полезно использовать AsyncTask при получении данных с сервера в виде:
/**
* Background Async Task to Load all product by making HTTP Request
* */
class LoadAllItems extends AsyncTask<String, String, String> {
/**
* Before starting background thread Show Progress Dialog
* */
@Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(YourActivityName.this);
pDialog.setMessage("Loading Items. Please wait...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
/**
* getting All products from url
* */
protected String doInBackground(String... args) {
//your method that get data from server which getlistOfMasjids()
//Then save them in arrays or ArrayList, after that you can save them in listview
return null;
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(String file_url) {
// dismiss the dialog after getting all products
listview();
pDialog.dismiss();
}
}
Как вы отметили в методе doInBackground, вы можете использовать вашу функцию getlistOfMasjids(), после этого вы можете сохранить данные в массивах или Arraylist, чтобы поместить их в просмотр списка.
для списка в методе onPostExecute() это выглядит так:
public void listview(){
adapter =new SelectMasjidAdapter(this, array1, array2,array3);
lv.setAdapter(adapter);
}
для списка просмотра списка действий:
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
showInfo(position); // here you can put the call for another class or details item for specific position
}
});
Надеюсь, что это будет полезно,
Android выложил очень хорошую документацию по ANR и тому, как заставить приложение реагировать... Я часто ссылаюсь на этот документ, когда нажимаю ANR.
http://developer.android.com/training/articles/perf-anr.html
Я установил правило для себя, так как оставить поток пользовательского интерфейса только для обработки пользовательского интерфейса, дБ, IO, сети должны быть выполнены на потоке, не являющемся пользовательским интерфейсом... Я использую поток и обработчики... Тем не менее, иногда необходимо создать поток пользовательского интерфейса подождите, в этом случае у вас нет выбора, но по возможности избегайте такой ситуации...
Вы также можете использовать метод runOnUiThread для обновления пользовательского интерфейса из других не-пользовательских потоков...