问题描述
我已使用Retrofit2进行文件下载。我无法使用进度值更新ProgressBar。我有进步的价值。因此没有问题。当我将进度值设置为进度条时,UI中未显示该值。
我说的是RecyclerView适配器内部的进度条。
下面是我的改造电话, 单击RecyclerView内的项目时,将调用此方法。
private void downloadFileFromServer(String otpapi,String userName,String password,String code,String vmFileName,String filePath,String vmFileSize,int position,CircularProgressBar circularProgress) {
GetDataForApiCall getDataForApiCall= RetrofitInstance.getRetrofit(url,otpapi,context).create(GetDataForApiCall.class);
Call<ResponseBody> downloadVoicemail= getDataForApiCall.downloadVoiceMail(userName,password,code,vmFileName);
this.circularProgressBar=circularProgress;
this.circularProgressBar.setIndeterminate(false);
this.circularProgressBar.setProgress(0);
this.circularProgressBar.setMax(100);
this.circularProgressBar.setonClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AndroidLogger.log(5,"onClick","circularProgressBar onClick executed!!");
Toast.makeText(context,"cancel clicked",Toast.LENGTH_LONG).show();
cancelDownload = true;
}
});
downloadVoicemail.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call,Response<ResponseBody> response) {
boolean downloadResult = writeResponseBodyTodisk(response.body(),vmFileSize,filePath);
if(downloadResult) {
Toast.makeText(context,"File downloaded",Toast.LENGTH_SHORT).show();
updateVoiceMailFilePath(position,filePath);
updateViews(position);
}else {
deleteVoiceMailFileFromLocalSystem(filePath);
updateViews(position);
}
}
@Override
public void onFailure(Call<ResponseBody> call,Throwable t) {
}
});
}
private boolean writeResponseBodyTodisk( ResponseBody body,String fileSize,String filePath) {
try {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[8192];
//long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
long lengthOfFile = Long.parseLong( String.format( "%.0f",Double.parseDouble(fileSize )) ) * 1024;
AndroidLogger.log(5,TAG,"filesize"+fileSize + "length of file"+lengthOfFile);
inputStream = body.byteStream();
outputStream = new FileOutputStream(filePath);
while (true) {
int read = inputStream.read(fileReader);
if(cancelDownload){
inputStream.close();
return false;
}
if (read == -1) {
AndroidLogger.log(5,"-1 value so break");
break;
}
outputStream.write(fileReader,read);
fileSizeDownloaded += read;
if(lengthOfFile >0) {
AndroidLogger.log(5,"FileSize downloaded"+ fileSizeDownloaded);
int progress = (int) (fileSizeDownloaded * 100 / lengthOfFile);
AndroidLogger.log(5,"Length of file"+ lengthOfFile);
AndroidLogger.log(5,"Progress"+ progress);
this.circularProgressBar.setProgress(progress);
update(progress);
}
AndroidLogger.log(5,"file download: " + fileSizeDownloaded + " of " + fileSize);
}
outputStream.flush();
return true;
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}
我还尝试了Listener更新值,因为在其他线程上进行了改造调用。因此,对于更新UI,我使用了没有帮助的监听器。
我正在使用Retrofit2进行API调用。因此,为了在所有活动中更新UI,我使用了接口侦听器。这非常适合所有活动。但是,当我在RecyclerView Adapter类中尝试相同的操作时,无法更新进度栏。在调用api之前,我已将进度栏设置为0,最大设置为100。
以下情况可以正常工作
API调用之前的循环ProgressBar设置为零
下载完成后的圆形ProgressBar将变为刻度线
以下内容不起作用
带有加载指示的圆形ProgressBar
注意:仅当使用Retrofit2进行API调用时,我才面临此问题。如果我使用普通的HTTPUrlConnection在Asynctask中进行API调用,则可以正常进行加载。
我已经通过以下代码检查了是否在主线程上进行了进度更新
if(Looper.myLooper() == Looper.getMainLooper()) {
circularProgressBar.setProgress(progress);
}
如果满足以上条件。即使进度栏未更新。
我也在下面尝试过
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = new Runnable() {
@Override
public void run() {
AndroidLogger.log(5,"Running on UI thread");
circularProgressBar.setProgress(progress);
}
};
mainHandler.post(myRunnable);
}
我将其放置在while循环内的writeResponseBodyTodisk方法内, 但这只被调用了两次,进度条没有更新。
我评论了该部分,进度条加载将变为刻度线。之后,当我尝试下载时,一旦下载完成就可以看到进度条中的100%下载已完成。在进度之前,更新百分比未反映。
请有人帮我解决这个问题。
谢谢。
解决方法
需要在UI线程中进行UI更新。从背景线程(即AsyncTask
)设置颜色实际上不会更新UI,因为这不是在UI线程中发生的。有几种方法可以更新UI中的进度颜色。我建议您将一个接口与一个回调函数一起使用,以便您可以调用该回调函数以从实现该接口的片段活动中更新UI。这是一个澄清。
让我们先声明一个接口。
public interface UIUpdater {
void updateUI(int progressValue);
}
现在在您要更新UI的活动或片段中实现此接口。
public class MainActivity extends Activity implements UIUpdater {
@Override
public void updateUI(int progressValue) {
// Do the UI update here.
circularProgress.setProgress(progressValue); // Or anything else that you want to do.
}
}
您要修改初始化AsyncTask
的构造函数,以使UIUpdater
类作为参数传递。
public class YourAsyncTask extends AsyncTask<String,Void,String> {
UIUpdater listener;
public YourAsyncTask(UIUpdater listener) {
this.listener = listener;
}
}
因此您可以使用以下方法从活动/片段中调用AsyncTask
。
YourAsyncTask myTask = new YourAsyncTask(this); // When you are passing this from activity,you are implicitly passing the interface that it implemented.
myTask.execute();
现在,从异步任务开始,在发布进度时,调用侦听器功能,以便使用活动/片段的UI线程更新UI。
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
try {
listener.updateUI(values[0]);
}
}
希望您能明白。
,您的问题不是您使用的是RecyclerView,而是您使用的不是正确的问题。
既不是适配器,也不是视图(RecyclerView),甚至也不是ViewHolder的责任来确定此处的任何内容。
- 我假设您有一个带有进度条的ViewHolder类型。
- 我假设您有一个
ListAdapter<T,K>
和相应的DiffUtilCallback
实现。 - 我认为进度是在其他地方处理/报告的(在您的存储库中,通过viewModel或Presenter,通过useCase,Interactor甚至是普通的ViewModel)。
- 您的适配器现在只需要等待即可。
- 更新进度时,请准备要显示的列表,以便更新进度已更改的项目。
- 之后,您将此新列表(具有新进度)提交给适配器。
- 您的DiffUtil进行计算(也可以是异步的!)
- 您的RecyclerView进行了神奇的更新,无需破解任何东西。
如果其中任何一个都不正确,请进行必要的调整,以便可以正确测试您的代码,并更好地区分每段代码的关注点。
以这种方式进行思考,假设您拥有所有这些,并且您更新的“进度值”中存在一个小错误。在ViewModel或UseCase / interactor中的小功能中搜索将Retrofit值转换为<Thing>
或在意粉回调代码中的所有位置,这会更容易些?
谢谢大家为我提供帮助!
此答案帮助我更新了Circular ProgressBar https://stackoverflow.com/a/42119419/11630822
import math
z = []
for i in range(n):
x = float(input())
z.append(math.sqrt(x))
for i in z:
print(i)
Progressbody类,
2.0
1.0
1.4142135623730951
1.7320508075688772
public static Retrofit getDownloadRetrofit(String baseUrl,DownloadVoicemailListener listener) {
return new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(getOkHttpDownloadClientBuilder(listener).build())
.build();
}
private static OkHttpClient.Builder getOkHttpDownloadClientBuilder(DownloadVoicemailListener listener) {
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder().connectionSpecs(Collections.singletonList(getConnectionSpec()));
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
if (!releaseMode) {
logging.level(HttpLoggingInterceptor.Level.BODY);
httpClientBuilder.addInterceptor(logging);
}
httpClientBuilder.connectTimeout(20,TimeUnit.SECONDS);
httpClientBuilder.writeTimeout(0,TimeUnit.SECONDS);
httpClientBuilder.readTimeout(5,TimeUnit.MINUTES);
httpClientBuilder.addInterceptor(new Interceptor() {
@NotNull
@Override
public Response intercept(@NotNull Interceptor.Chain chain) throws IOException {
if (listener == null) return chain.proceed(chain.request());
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.body(new ProgressResponseBody(originalResponse.body(),listener))
.build();
}
});
return httpClientBuilder;
}
public class ProgressResponseBody extends ResponseBody {
private final String TAG=ProgressResponseBody.class.getSimpleName();
private ResponseBody responseBody;
private BufferedSource bufferedSource;
public ProgressResponseBody(ResponseBody responseBody,DownloadVoicemailListener progressListener) {
this.responseBody = responseBody;
progressListener.readFile(responseBody);
}
@Override public MediaType contentType() {
return responseBody.contentType();
}
@Override public long contentLength() {
return responseBody.contentLength();
}
@NotNull
@Override public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
private Source source(Source source) {
return new ForwardingSource(source) {
long totalBytesRead = 0L;
@Override public long read(Buffer sink,long byteCount) throws IOException {
return byteCount;
}
};
}
}