2016-03-02 3 views
1

У меня есть другая проблема или ошибка в приложении для Android. Извините, что я новичок в разработке Android, но это моя страсть, и я буду упорствовать.Обновить recyclerview при нажатии кнопки

В любом случае, мой код разбивается на вызов метода в моем классе CustomAdapter.

Это мой CustomAdapter класс, где происходит сбой:

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.CustomViewHolder> { 
private ArrayList<UserInfo> arrayList = new ArrayList<UserInfo>(); 
float ratings; 

public CustomAdapter(ArrayList<UserInfo> arrayList){ 
    this.arrayList = arrayList; 
} 

@Override 
public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.userslayout, parent, false); 
    CustomViewHolder customViewHolder = new CustomViewHolder(view); 
    return customViewHolder; 
} 

@Override 
public void onBindViewHolder(final RecyclerViewHolder holder, int position) { 
    final UserInfo userInfo = arrayList.get(position); 
    holder.username.setText(userInfo.getMusicName()); 
    holder.forename.setText(userInfo.getMusicCategory()); 
    holder.surname.setText(userInfo.getFileType()); 
    // THE APP CRASHES WHEN I CLICK THIS BUTTON. THINGS UPDATE IN THE 
    // DATABASE - BUT THE RECYCLERVIEW CRASHES AND THE APP CRASHES 
    holder.buttonRefresh.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      String pressed = Integer.toString(userInfo.getPressedName()); 
      UserListRecycler userListRecycler = new UserListRecycler(); 
      UserListRecycler.UserPressedAsync userPressedAsync = userListRecycler.new UserPressedAsync(); 
      userPressedAsync.execute(username, pressed); 
     } 
    }); 
} 

}

Это мой UserListRecycler класс - основной класс, а внутренний класс UserPressedAsync

public class UserListRecycler extends Fragment { 
    RecyclerView recyclerView; 
    static UserAdapter adapter; 
    RecyclerView.LayoutManager layoutManager; 
    ArrayList<UserInfo> list; 


    @Override 
    public void onCreate(Bundle savedInstanceState){ 
     super.onCreate(savedInstanceState); 
    } 

    @Nullable 
    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     View rootView = inflater.inflate(R.layout.userlistGUI, container, false); 
     recyclerView = (RecyclerView) rootView.findViewById(R.id.reUsers); 
     recyclerView.setHasFixedSize(true); 
     list = new ArrayList<UserInfo>(); 
     // Instantiate new adapter here 
     adapter = new MusicRecyclerAdapter(list); 
     LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity()); 
     linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); 
     recyclerView.setLayoutManager(linearLayoutManager); 
     // Sets the adapter here 
     recyclerView.setAdapter(adapter); 
     adapter.notifyDataSetChanged(); 
     return rootView; 
    } 

    @Override 
    public void onStart() { 
     super.onStart(); 
     populateRecyclerList(); 
    } 


    public void populateList(){ 
     PopulateUsers userList = new PopulateUsers(list, adapter, recyclerView); 
     userList.execute(); 
    } 

    public class UserPressedAsync extends AsyncTask<String, String, String> { 
    @Override 
    protected String doInBackground(String... params) { 
     // REST STUFF HAPPENS HERE 
    } 

    @Override 
    protected void onPostExecute(String postData) { 
     // CRASHES HERE!!! 
     populateRecyclerList(); 
    } 
} 
} 

Это мой класс PopulateUsers - где данные с моего сервера извлекаются через REST

public class PopulateUsers extends AsyncTask<String, String, String> { 
    static ArrayList<UserInfo> list; 
    UserAdapter adapter; 
    RecyclerView recyclerView; 

public PopulateUsers(ArrayList<UserInfo> list, UserAdapter adapter, RecyclerView recyclerView) { 
    this.list = new ArrayList<UserInfo>(); 
    this.adapter = new UserAdapter(); 
    this.list = list; 
    this.adapter = adapter; 
    this.recyclerView = recyclerView; 
} 

@Override 
protected void onPreExecute() { 
    super.onPreExecute(); 
} 

@Override 
protected String doInBackground(String... params) { 
    // `REST` Activity happens here 
} 

@Override 
protected void onPostExecute(String s) { 
    try { 
     list.clear(); 
     JSONArray jsonArray = new JSONArray(s); 
     for (int i = 0; i < jsonArray.length(); i++) { 
      String forename = jsonArray.getJSONObject(i).getString("forename"); 
      String surname = jsonArray.getJSONObject(i).getString("surname"); 
      String nationality = jsonArray.getJSONObject(i).getString("nationality"); 
      UserInfo userInfo = new UserInfo(forename, surname, nationality); 
      list.add(userInfo); 
     } 
     recyclerView.setAdapter(adapter); 
     adapter.notifyDataSetChanged(); 
    } catch (JSONException e) { 
     e.printStackTrace(); 
    } 
} 
} 

В основном, когда нажимается кнопка вещи обновляются в backend, т.е. phpmyadminmysql сервер - но приложение не обновляется с обновленным списком, как он выходит из строя. Это что-то сделать с помощью метода populateList() в моем UserListRecycler классе, он указывает на ошибку в recyclerView.setAdapter(adapter); в PopulateUsers классе - именно в onPostExecute(), как NullPointerException, но он отлично работает, когда UserListRecycler класс первый экземпляр и загружен. Так почему он падает, когда я вызываю метод populateList()?

Это моя трассировка стеки:

java.lang.NullPointerException 
      at lukasz.usersapp.PopulateUsers.onPostExecute(PopulateUsers.java:67) 
      at lukasz.usersapp.PopulateUsers.onPostExecute(PopulateUsers.java:27) 
      at android.os.AsyncTask.finish(AsyncTask.java:631) 
      at android.os.AsyncTask.access$600(AsyncTask.java:177) 
      at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644) 
      at android.os.Handler.dispatchMessage(Handler.java:99) 
      at android.os.Looper.loop(Looper.java:137) 
      at android.app.ActivityThread.main(ActivityThread.java:5103) 
      at java.lang.reflect.Method.invokeNative(Native Method) 
      at java.lang.reflect.Method.invoke(Method.java:525) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 
      at dalvik.system.NativeStart.main(Native Method) 

Это действительно много значит, на самом деле отсыпаться для себя, если кто-то может помочь мне решить эту проблему. Это ужасная ошибка!

ответ

1

Ошибка в основном связана с тем, что вы получаете доступ к вашему recyclerview, пока его адаптер еще не создан. Это потому, что вы показываете recyclerview в основном потоке, настраивая его в фоновом режиме. В вашем случае два потока не синхронизируются, поэтому, пока вы нажимаете кнопку, recyclerview еще не настроен должным образом, что дает вам исключение нулевого указателя.

Я предлагаю вам найти работу по получению данных для отображения, а затем настроить свой recyclerview перед его отображением.

Это не очень хорошая идея иметь веб-вызов с использованием asynctask, вам обычно приходится использовать библиотеки, такие как retrofit, gson, rxandroid и т. Д., Но поскольку вы только начинаете, вам нужно сделать это с помощью asynctask.

Я предлагаю вам сделать ваш recyclerView.setAdapter(adapter) в основном потоке, как в вашем onCreate, так как у вас нет данных, необходимых для отображения recyclerview, вы должны либо установить его с фиктивными данными первого или, более предпочтительно, дон Покажите это еще раз, сначала отобразите индикатор выполнения, чтобы сообщить пользователю, что он еще не загружен.

Если вы сделаете то, что я предложил, вам не нужно называть recyclerView.setAdapter(adapter) в вашей асинтете, вам просто нужно уведомить его о том, что у вас есть изменения, что, конечно же, ваше adapter.notifyDataSetChanged().

+0

Так как я могу исправить эту ужасную ошибку, пожалуйста? Я буквально не могу спать, и я продолжаю стучать головой о стену. –

+0

Есть ли способ, которым вы можете исправить мой код? Наверняка, он двигает линию здесь и там –

+0

, откуда приходит ваш json? Из api? – hehe

0

recyclerView.setAdapter(adapter);

Вы вызываете, что в два раза.Почему бы просто не оставить его в onCreateView и удалить его с postExecute? Просто оставьте adapter.notifyDataSetChanged() там в postExecute и удалите его из onCreateView. Есть ли разница?

Я пользуюсь библиотекой Volley для этих задач и довольно прост в использовании. Выглядит более аккуратно, чем ASyncTask. Может быть, вы могли бы попробовать.

Посмотрите на этой странице: http://developer.android.com/training/volley/index.html

Если вы клонировать Volley library (папка получает загружен), импортировать его в Android Studio через New>Import MODULE>path to library. Затем вам нужно положить compile project(':volley') в ваше приложение в Build gradle под зависимостями - так это выглядит примерно так:

dependencies { 
    compile fileTree(dir: 'libs', include: ['*.jar']) 
    compile 'com.android.support:support-v4:23.1.1' 
    compile 'com.android.support:appcompat-v7:23.1.1' 
    compile 'com.android.support:design:23.1.1' 
    compile project(':volley') 
} 

Это как мой метод, чтобы загрузить JSON из службы PHP выглядит следующим образом:

public void downloadEvents(String urlService) { 


    RequestQueue requestQueue = Volley.newRequestQueue(getActivity()); //getActivity because I'm calling it from a fragment, if called in Activity, use 'this' 
    JsonArrayRequest jsonArrayRequest = new JsonArrayRequest 
      (Request.Method.GET, urlService, null, new Response.Listener<JSONArray>() { 

       @Override 
       public void onResponse(JSONArray response) { 

        try { 


         for (int i = 0; i < response.length(); i++) { 
          JSONObject eventJson = response.getJSONObject(i); 

          String title = eventJson.getString("EventTitle"); 
          String body = eventJson.getString("EventBody"); 
          double price = Double.parseDouble(eventJson.getString("EventPrice")); 
          String date = eventJson.getString("EventDate"); 
          String time = eventJson.getString("EventTime"); 
          String place = eventJson.getString("EventPlace"); 
          String organ = eventJson.getString("Organization"); 

          Event event = new Event(title, body, price, date, time, durationStr, place, organ); 
          theEvents.add(event); 
          rAdapter.notifyItemInserted(i); 
         } 


         if (swipeRefreshLayout.isRefreshing()) { 
          swipeRefreshLayout.setRefreshing(false); 
         } 
        } catch (JSONException e) { 
         e.printStackTrace(); 
        } 


       } 


      }, new Response.ErrorListener() { 

       @Override 
       public void onErrorResponse(VolleyError error) { 
        Log.e("VOLLEY ERROR", "" + error); 

       } 
      } 
      ); 

    requestQueue.add(jsonArrayRequest); //important to add that otherwise the download wont be initiated. 
} 

Вы может видеть, как я обрабатываю свой JSON и где могу использовать такие вещи, как swipeRefreshLayout. Обратите внимание, что это все еще сделано asynchronously.

Важно, чтобы пойти и проверить свой JSON responses и посмотреть, начинается ли ответ с объекта или массива. Затем соответствующим образом настройте код Volley. Это в основном меняет большинство Array ключевых слов в коде на Object (если вы получаете объект JSON, содержащий ваши данные). Пример:

JsonArrayRequest jsonArrayRequest = new JsonArrayRequest 
      (Request.Method.GET, urlService, null, new Response.Listener<JSONArray>() { 

становится

JsonObjectRequest jsonObjectRequest = new JsonObjectRequest 
      (Request.Method.GET, urlService, null, new Response.Listener<JSONObject>() { 

....

В моей OnCreate, перед использованием downloadEvents("http//example.com/service.php"); Я сделал все RecyclerView и адаптер код:

 recyclerView = (RecyclerView) fragmentView.findViewById(R.id.rv); 
    LinearLayoutManager llm = new LinearLayoutManager(getActivity()); 
    recyclerView.setLayoutManager(llm); 
    theEvents = new ArrayList<>(); 
    rAdapter = new RecyclerAdapter(theEvents, recyclerView); 
    recyclerView.setAdapter(rAdapter); 

Метод загрузки просто уведомляет адаптер.

+0

Как использовать volley, есть ли каким-либо образом я могу преобразовать свой код в залп? –

+0

Я дал вам почти полный учебник Volley. Надеюсь, это просто, и это поможет вам. То, как я это сделал (на данный момент), этот метод - это то, где я устанавливаю данные, поэтому мой список и адаптер находятся в одном классе (фрагменте). Вот почему я просто добавляю в список и уведомляю об этом свой адаптер. – iBobb

+0

Спасибо, но что такое 'Event event = new Event (название, тело, цена, дата, время, продолжительностьStr, место, орган);'? Кроме того, если бы я должен был ввести код волейбола в отдельный класс, будет ли он работать? –

Смежные вопросы