AbhayBohra AbhayBohra - 3 months ago 27
Android Question

Update SeekBar at particular position within a RecyclerView

I am creating an application for downloading songs. In my application, there is a

RecyclerView
with a list of songs and with every song there is a
SeekBar
below that shows the download progress. When I hit the download button, it works fine but it updates the wrong
SeekBar
. I want to update a particular
SeekBar
at a particular position. Here is my adapter for the
RecyclerView
:

@Override
public SongListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

itemViewLayout = LayoutInflater.from(parent.getContext()).inflate(R.layout.song_list_single_list, null);
holder = new ViewHolder(itemViewLayout);
return holder;
}

@Override
public void onBindViewHolder(SongListAdapter.ViewHolder holder, final int position) {
mSongListModel = mSongListArray.get(position);
holder.downloadLink.setText(mSongListModel.getDownloadLink());
holder.downloadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new DownloadFileAsync().execute(String.valueOf(position));
}
});
}

@Override
public int getItemCount() {
return mSongListArray.size();
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("Permission: " + permissions[0] + "was " + grantResults[0]);
//resume tasks needing this permission
//downloadStreamingAudio();
}
}

public class ViewHolder extends RecyclerView.ViewHolder {

private TextView downloadLink;
private SeekBar downloadProgress;
private TextView downloadButton;

public ViewHolder(View itemView) {
super(itemView);
downloadLink = (TextView) itemView.findViewById(R.id.downloadLink);
downloadButton = (TextView) itemView.findViewById(R.id.downloadButton);
downloadProgress = (SeekBar) itemViewLayout.findViewById(R.id.downloadProgress);
}
}

class DownloadFileAsync extends AsyncTask<String, String, String> {
ProgressDialog pDialog;
Uri uri;
String timeStamp;

@Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(c);
pDialog.setMessage("Downlaoding Audio...");
pDialog.setCancelable(true);
pDialog.show();
}

@Override
protected String doInBackground(String... params) {

try {
if (isStoragePermissionGranted()) {
File root = Environment.getExternalStorageDirectory();

File dir = new File(root.getAbsolutePath() + "/AppDownloads/");

timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
System.out.println("Time Stamp::----" + timeStamp);

try {
if (dir.exists() == false) {
dir.mkdirs();
}

RootFile = new File(dir, timeStamp + ".mp3");

} catch (Exception e) {
e.printStackTrace();
}
}

InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
int count;

System.out.println("DownloadUrl::::----"+mSongListArray.get(Integer.parseInt(params[0])).getDownloadLink());
URL url = new URL(mSongListArray.get(Integer.parseInt(params[0])).getDownloadLink());
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// this will be useful so that you can show a tipical 0-100% progress bar
int lenghtOfFile = connection.getContentLength();

// downlod the file
input = new BufferedInputStream(url.openStream());
output = new FileOutputStream(RootFile);

byte data[] = new byte[1024];

long total = 0;

while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
//publishProgress((int)(total*100/lenghtOfFile));
updateDownloadProgress((int) (total * 100 / lenghtOfFile));
output.write(data, 0, count);
}

output.flush();
output.close();
input.close();

} catch (Exception e)

{

Log.d("Error....", e.toString());
}

return null;
}

@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if (pDialog.isShowing())

pDialog.dismiss();

Toast.makeText(c, "Download Complete", Toast.LENGTH_LONG).show();
}
}

public boolean isStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= 23) {
if (c.checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
System.out.println("Permission is granted");
return true;
} else {

System.out.println("Permission is revoked");
ActivityCompat.requestPermissions((Activity) c, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
} else {
System.out.println("Permission is granted");
return true;
}
}

private void updateDownloadProgress(int progress) {
holder.downloadProgress.setProgress(progress);

}


Here is my layout file for the
RecyclerView
:

<?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="match_parent"
android:orientation="horizontal"
android:padding="10dp">

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

<TextView
android:id="@+id/downloadLink"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="10dp" />

<TextView
android:id="@+id/downloadButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/custom_button"
android:text="DOWNLOAD"
android:textSize="15sp" />

<TextView
android:id="@+id/playButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:background="@drawable/custom_button"
android:text="PLAY"
android:textSize="15sp"
android:visibility="gone" />
</LinearLayout>

<SeekBar
android:id="@+id/downloadProgress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/LinearLayout"
android:layout_marginTop="10dp" />
</RelativeLayout>


Any suggestions on how to achieve this?

Answer

Your program is tightly-coupled. I recommend you to split program into 3 peaces. And Call notifyItemChanged at onProgressUpdated

Below code is examples.

RecyclerView.Adapter like this:

public class SongListAdapter extends RecyclerView.Adapter<SongListAdapter.ViewHolder> {
    ...

    private final Listener mListener;
    private final Map<Integer, Integer> progressMap = new HashMap<>();

    ...

    interface Listener {
        void onClickDownload(int position, SongListModel songListModel);
    }

    public SongListAdapter(Listener listener) {
        mListener = listener;
    }

    @Override
    public void onBindViewHolder(SongListAdapter.ViewHolder holder, final int position) {
        ...
        int progress = progressMap.containsKey(position) ? progressMap.get(position) : 0;
        holder.downloadProgress.setProgress(progress);
        holder.downloadButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SongListModel songListModel = mSongListArray.get(position);
                mListener.onClickDownload(position, songListModel);
            }
        });
    }
    ...

    public void setProgress(int position, int progress) {
        progressMap.put(position, progress);
    }
    ...
}

DownloadFileAsync like this:

public class DownloadFileAsync extends AsyncTask<String, String, String> {

    ...
    private final Callback mCallback;
    private final int mPosition;
    private final SongListModel mSongListModel;

    interface Callback {
        void onDownloadStarting();
        void onProgressUpdated(int progress, int position, SongListModel songListModel);
        void onDownloadCompleted();
    }

    public DownloadFileAsync(Callback callback, int position, SongListModel songListModel) {
        mCallback = callback;
        mPosition = position;
        mSongListModel = songListModel;
    }

    @Override
    protected void onPreExecute() {
        mCallback.onDownloadStarting();
    }

    @Override
    protected String doInBackground(String... params) {
        try {
            ...
            while ((count = input.read(data)) != -1) {
                ...
                mCallback.onProgressUpdated((int) (total * 100 / lenghtOfFile), mPosition, mSongListModel);
                ...
                if (isCancelled()) {
                    break;
                }
            }
            ...
        }
        return null;
    }

    ...

    @Override
    protected void onPostExecute(String s) {
        mCallback.onDownloadCompleted(s);
    }
}

Activity like this:

public class MainActivity extends AppCompatActivity
        implements SongListAdapter.Listener, DownloadFileAsync.Callback {
    ...

    private RecyclerView mRecyclerView;
    private SongListAdapter mAdapter;
    private DownloadFileAsync mDownloadFileAsync;
    private ProgressDialog pDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        mRecyclerView = (RecyclerView)findViewById(...);
        mAdapter = new SongListAdapter(this);
        mRecyclerView.setAdapter(adapter);
        ...
    }

    @Override
    public void onClickDownload(int position, SongListModel songListModel) {
        if (mDownloadFileAsync != null) {
            //TODO currentry downloading. Insert error handing.
            return;
        }

        mDownloadFileAsync = new DownloadFileAsync(this, position, songListModel);
        mDownloadFileAsync.execute();
    }


    @Override
    public void onDownloadStarting() {
        pDialog = new ProgressDialog(this);
        pDialog.setMessage("Downlaoding Audio...");
        pDialog.setCancelable(true);
        pDialog.show();
    }

    @Override
    public void onProgressUpdated(int progress, int position, SongListModel songListModel) {
        mAdapter.setProgress(position, progress);

        SongListAdapter.ViewHolder holder = mRecyclerView.findViewHolderForAdapterPosition(position);
        if (holder != null) {
            holder.downloadProgress.setProgress(progress);
        }
    }

    @Override
    public void onDownloadCompleted(String s) {
        mDownloadFileAsync = null;
        if (pDialog.isShowing()) {
            pDialog.dismiss();
        }
        Toast.makeText(this, "Download Complete", Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // If you want to stop downloading when activity is closing, call cancel method.
        if (mDownloadFileAsync != null) {
            mDownloadFileAsync.cancel(true);
        }
    }

    ...
}
Comments