2017-02-06 3 views
1

У меня есть listview с пользовательскими ячейками, в которых есть хронометр, текстовое изображение и переключатель. Это listview заполняется данными dumby из ArrayList. Всякий раз, когда нажимается переключатель, он всегда влияет на последний элемент в списке, а не на тот, который я намеревался нажать.Android - OnClickListener в элементе listview влияет только на последнюю строку

Вот мой заказ timeTrackCellAdapter класс

public class timeTrackCellAdapter extends ArrayAdapter { 
    private final Activity activity; 
    private final List timeParams; 
    TimeView tView = null; 
    View rowView; 
    //Constructor 
    public timeTrackCellAdapter(Activity activity, List objects){ 
     super(activity, R.layout.cell_layout, objects); 
     this.activity = activity; 
     this.timeParams = objects; 
    } 
    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     rowView = convertView; 


     if(rowView == null) 
     { 
      // Get a new instance of the row layout view 
      LayoutInflater inflater = activity.getLayoutInflater(); 
      rowView = inflater.inflate(R.layout.cell_layout, null); 

      // Hold the view objects in an object, 
      // so they don't need to be re-fetched 
      tView = new TimeView(); 
      tView.timer = (Chronometer) rowView.findViewById(R.id.timeTracker); 
      tView.jobText = (TextView) rowView.findViewById(R.id.secondaryRowText); 
      tView.jobSwitch = (Switch) rowView.findViewById(R.id.timeSwitch); 

      // Cache the view objects in the tag, 
      // so they can be re-accessed later 
      rowView.setTag(tView); 
     } else { 
      tView = (TimeView) rowView.getTag(); 
     } 

     // Transfer the job/time from the data object 
     // to the view objects 
     final timeTrackCell currentTime = (timeTrackCell) timeParams.get(position); 

     tView.timer.setBase(currentTime.getChronometerTime()); 
     tView.jobText.setText(currentTime.getJobString()); 
     tView.jobSwitch.setChecked(currentTime.getSwitchPosition()); 


     //OnClick for switch toggle 
     tView.jobSwitch.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 

       Boolean newCheck = tView.jobSwitch.isChecked(); 
       System.out.println(tView.jobText.getText()); 

       //If newCheck returns true, the switch is being turned on 
       //If newCheck returns false, the switch is being turned off 
       tView.timer.stop(); 
       System.out.println(newCheck); 
       if(newCheck){ 

        tView.jobSwitch.setChecked(true); 
        tView.jobText.setText(currentTime.getJobString()); 
        tView.timer.setBase(currentTime.getChronometerTime()); 
        tView.timer.start(); 


       }else{ 

        tView.timer.stop(); 
        tView.jobSwitch.setChecked(false); 
        tView.jobText.setText(currentTime.getJobString()); 
        tView.timer.setBase(currentTime.getChronometerTime()); 

       } 


      } 
     }); 

     return rowView; 

    } 


    protected static class TimeView { 
     protected Chronometer timer; 
     protected TextView jobText; 
     protected Switch jobSwitch; 
    } 
} 

Вот мой timeTrackCell класс, который имеет все мои получает и наборы для моего адаптера

public class timeTrackCell { 
    private boolean switchPosition; 
    private long chronometerTime; 
    private String jobString; 

    public timeTrackCell(boolean switchPosition, long chronometerTime, String jobString){ 
     this.switchPosition = switchPosition; 
     this.chronometerTime = chronometerTime; 
     this.jobString = jobString; 
    } 
    //sets 
    public void setSwitchPosition(boolean switchPosition){ 
     this.switchPosition = switchPosition; 
    } 
    public void setChronometerTime(long chronometerTime){ 
     this.chronometerTime = chronometerTime; 
    } 
    public void setJobString(String jobString){ 
     this.jobString = jobString; 
    } 
    //gets 
    public boolean getSwitchPosition(){ 
     return switchPosition; 
    } 
    public long getChronometerTime(){ 
     return chronometerTime; 
    } 
    public String getJobString(){ 
     return jobString; 
    } 


} 

Вот файл XML для моих клеток, cell_layout

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:minHeight="140px" 
    > 
    <!--140px Seems to be the right height for 7 cells per page--> 
    <!-- Block for custom listview items --> 
    <Chronometer 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:id="@+id/timeTracker" 
     android:layout_gravity="left" 
     android:textSize="25sp" 
     android:paddingLeft="10px" 
     android:layout_centerVertical="true"> 
    </Chronometer> 

    <TextView 
     android:id="@+id/secondaryRowText" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:layout_below="@id/timeTracker" 
     android:textSize="15sp" 
     android:paddingLeft="10px" 
     android:paddingTop="30px" 
     > 
    </TextView> 

    <Switch 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:id="@+id/timeSwitch" 
     android:gravity="right" 
     android:layout_centerVertical="true" 
     android:focusable="false" 
     android:clickable="false" 
     > 
    </Switch> 
</RelativeLayout> 

И вот класс java, который создает список и заполняет d ата. timeKeeping.java

public class timeKeeping extends AppCompatActivity { 
    public String empName = "Zach"; 
    private ListView lv; 
    //tempchange 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     ///// 
     //Button that shows who is logged in 
     setContentView(R.layout.activity_time_keeping2); 
     Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
     setSupportActionBar(toolbar); 
     FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); 
     fab.setImageResource(R.drawable.ic_temp_profile_image); 
     fab.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View view) { 
       String greetingString = "Welcome back, " + empName + "!"; 
       Snackbar.make(view, greetingString, Snackbar.LENGTH_LONG) 
         .setAction("Action", null).show(); 
      } 
     }); 
     getSupportActionBar().setDisplayHomeAsUpEnabled(true); 
     ///// 
     lv = (ListView) findViewById(R.id.timeList); 
     //DUMBY DATA TO TEST WITH 
     final List timeData = new ArrayList(); 
     Long testData = (long) 1000000; 
     String tempJobTest = "test job "; 
     for(int i = 0; i<5;i++){ 
      String nTempJobTest = tempJobTest + i; 
      timeData.add(new timeTrackCell(false, testData, nTempJobTest)); 

     } 


     lv.setAdapter(new timeTrackCellAdapter(this, timeData)); 

    } 
} 

Я довольно уверен, что моя проблема с моим OnClick в моем классе timeTrackCellAdapter, но если это не я могу предоставить больше кода. Любая помощь очень ценится!

+2

это будет проще для вас, если вы переформатируете свой список для recyclerview, поверьте мне – mayosk

ответ

2

Вы ссылаетесь на ТОЧКУ tView переменную в каждом onView. Поскольку переменная находится в области класса, а не в области метода. Если вы создаете 5 строк, то первая строка использует переменную класса, а также вторую строку (теряя ссылку от первой) и т. Д.

Именно поэтому, когда вы нажимаете на любую строку, вы изменяете последнюю добавленную строку.

Решение может быть сделано только в локальной переменной внутри метода. НО, я рекомендую вам использовать RecyclerView, который является прецедентом ListView.

Во всяком случае, сначала удалить переменную из класса:

public class timeTrackCellAdapter extends ArrayAdapter { 
    private final Activity activity; 
    private final List timeParams; 
    View rowView; 
    .... 

И затем, создайте его внутри метода:

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 
    rowView = convertView; 
    TimeView tView; 
+0

Если я не объявляю tView как final, тогда как я могу ссылаться на него в моем setOnClickListener? – Joris

+0

Почему вы хотите, чтобы это было окончательным? В вашем коде не объявлено окончательным .. – adalPaRi

+0

Потому что это дало мне ошибку «Переменная« tView »открывается из внутреннего класса, должна быть объявлена ​​окончательной» – Joris

-2
@Override 
public View getView(int position, View convertView, ViewGroup parent 
{ 
    //OnClick for switch toggle 
    tView.jobSwitch.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 

      Boolean newCheck = tView.jobSwitch.isChecked(); 
      System.out.println(tView.jobText.getText()); 

      //If newCheck returns true, the switch is being turned on 
      //If newCheck returns false, the switch is being turned off 
      tView.timer.stop(); 
      System.out.println(newCheck); 
      if(newCheck){ 

       tView.jobSwitch.setChecked(true); 
       tView.jobText.setText(currentTime.getJobString()); 
       tView.timer.setBase(currentTime.getChronometerTime()); 
       tView.timer.start(); 


      }else{ 

       tView.timer.stop(); 
       tView.jobSwitch.setChecked(false); 
       tView.jobText.setText(currentTime.getJobString()); 
       tView.timer.setBase(currentTime.getChronometerTime()); 

      } 


     } 
    }); 

} 

Настройка слушателей в методе getView, вероятно, самая большая ошибка, которую вы могли бы применить к своему приложению. Эти методы получаются неявным образом, даже когда одно обновление происходит с любым элементом, вы, вероятно, создаете MEMORY-BLACKHOLE, потому что getView (int, View, ViewGroup) вызывается каждый раз при прокрутке или обновлении, поэтому вы, вероятно, объявили бы вашего слушателя тысячу раз в этом случае. Попробуйте другой фрагмент кода для настройки вашего слушателя, и это не решение, но строгая рекомендация

0

вызова notifyDataSetChanged();

tView.jobSwitch.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 

      Boolean newCheck = tView.jobSwitch.isChecked(); 
      System.out.println(tView.jobText.getText()); 

      //If newCheck returns true, the switch is being turned on 
      //If newCheck returns false, the switch is being turned off 
      tView.timer.stop(); 
      System.out.println(newCheck); 
      if(newCheck){ 

       tView.jobSwitch.setChecked(true); 
       tView.jobText.setText(currentTime.getJobString()); 
       tView.timer.setBase(currentTime.getChronometerTime()); 
       tView.timer.start(); 


      }else{ 

       tView.timer.stop(); 
       tView.jobSwitch.setChecked(false); 
       tView.jobText.setText(currentTime.getJobString()); 
       tView.timer.setBase(currentTime.getChronometerTime()); 

      } 
      //call this method 
      notifyDataSetChanged(); 

     }