0

I am new to Android programming, and have one question.

I am trying to access findViewById in my AsyncTask, but obviously, by default this will not be available, because I am not performing any actions against a View object.

I have found, a few articles, explaining how to solve this, but they are old, 5 years and up, and would like to know if this is still the correct approach? I am using android's data binding methodology, and this is supposed to replace findViewById calls, but I don't see how, in this scenario?

Is this way of solving still valid?

Here, is my code, in case there is a better solution. I am trying to access the progressbar in this view from within the AsyncTask

My Profile view:

<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
    <variable name="user" type="Models.User" />
    <variable name="viewActions" type="ViewModel.ProfileViewModel" />
</data>

<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal|top">

    <ProgressBar
        android:id="@+id/progressPostUser"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:visibility="gone"/>

    <include layout="@layout/main_toolbar"/>

    <ImageView
        android:id="@+id/imagePlaceHolder"
        android:layout_width="250dp"
        android:layout_height="150dp"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="5dp"
        android:src="@mipmap/ic_account"/>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp">

        <ImageButton
            android:id="@+id/btnOpenCamera"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginRight="10dp"
            android:src="@mipmap/ic_account"
            android:onClick="btnOpenCamper_OnClick"/>

        <ImageButton
            android:id="@+id/btnChooseImage"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:src="@mipmap/ic_view_list"/>

    </LinearLayout>

    <EditText
        android:layout_width="250dp"
        android:layout_height="50dp"
        android:hint="Name"
        android:text="@={user._name}"/>

    <EditText
        android:layout_width="250dp"
        android:layout_height="50dp"
        android:hint="Surname"
        android:text="@={user._surname}"/>

    <EditText
        android:layout_width="250dp"
        android:layout_height="50dp"
        android:hint="Email"
        android:text="@={user._email}"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Save"
        android:onClick="@{() -> viewActions.onSaveClicked(user)}"/>

</LinearLayout>

My Activity class:

public class ProfileActivity extends MenuActivity {

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

    ActivityProfileBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_profile);
    binding.setUser(new User());
    binding.setViewActions(new ProfileViewModel(this));

    //get the toolbar
    Toolbar tb = (Toolbar)findViewById(R.id.toolbarMain);
    setSupportActionBar(tb);
}
}

And the 'ViewModel' which handles events from the View.

public class ProfileViewModel {

private User mUser;
private Context mContext;

public ProfileViewModel(Context context){
    mContext = context;
}

public void onSaveClicked(User user) {
    String nameTest = user.get_name();
    String surnameTest = user.get_surname();

    Toast.makeText(mContext, user.get_name(), Toast.LENGTH_SHORT).show();
}
}

Here is my User class.

public class User extends BaseObservable {

public User() {

}

private String _name;
@Bindable
public String get_name() {
    return _name;
}
public void set_name(String _name) {
    this._name = _name;
    notifyPropertyChanged(BR._name);
}

private String _surname;
@Bindable
public String get_surname() {
    return _surname;
}
public void set_surname(String _surname) {

    this._surname = _surname;
    notifyPropertyChanged(BR._surname);
}

private String _email;
@Bindable
public String get_email() {
    return _email;
}
public void set_email(String _email) {

    this._email = _email;
    notifyPropertyChanged(BR._email);
}

private Bitmap _profileImage;
public Bitmap get_profileImage() {
    return _profileImage;
}
public void set_profileImage(Bitmap _profileImage) {
    this._profileImage = _profileImage;
}

public String toJsonString(){
    try{
        JSONObject jObject = new JSONObject();
        jObject.put("Name", get_name());
        jObject.put("Surname", get_surname());
        jObject.put("Email", get_email());
        jObject.put("ProfileImage", Base64.encodeToString(convertBitmapToBytes(), Base64.DEFAULT));
    } catch (Exception ex){
        Log.d("Error", "User.toJson");
    }
    return "";
}

@Override
public String toString() {
    return super.toString();
}

private byte[] convertBitmapToBytes(){
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    get_profileImage().compress(Bitmap.CompressFormat.PNG, 100, stream);
    return stream.toByteArray();
}

}

2 Answers2

0

You can do something like this. You can change information in the onPre, doInBackground, onPost --> This wouldnt matter much.

public class MainActivity extends AppCompatActivity {

    TestView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);

    }


    private class ASyncCall extends AsyncTask<Void, Void, Void> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            //What happens BEFORE everything in the background.
        }

        @Override
        protected Void doInBackground(Void... arg0) {
            textView.setText("set the text");
            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);

            //After you get everything you need from the JSON.

        }
    }
}
letsCode
  • 2,774
  • 1
  • 13
  • 37
  • This approach does not consider at all what I have so far?? Or am I mistaken here/ – android_monstertjie Feb 08 '18 at 18:51
  • i could also be mistaken what your question is. findViewById has to do with the ID over a view... right? You want to access a view inside async? – letsCode Feb 08 '18 at 18:52
  • Correct, but the calling code, that will call the AsyncTask class, is not within my activity class. My onSaveClicked function, which is called when the user clicks the save button, is defined within the ProfileViewModel class, not the activity class – android_monstertjie Feb 08 '18 at 18:54
  • I am upvoting your answer, as I still learned something from it, declaring a class in a class ;-) – android_monstertjie Feb 08 '18 at 18:55
  • you cant really do that with anything. findviewbyid has to be called where the layout is being called. you should have your aynsc in the activity, and call you can call methods inside the async in order to get what you need. – letsCode Feb 08 '18 at 19:02
  • "findviewbyid has to be called where the layout is being called", I disagree. Look at the answer I am about to post, where findViewById is not called where the View is 'inflated'... – android_monstertjie Feb 08 '18 at 19:10
0

I really did not like the idea of passing context, and the View around to different objects, and like to keep the view specific functionality within the activity class itself, so I implemented an interface, that I passed around, as follow:

Here is my interface:

public interface IProfiler {
    void ShowProgressbar();
    void HideProgressbar();
    void MakeToast();
}

My activity class implements this interface, as follow:

public class ProfileActivity extends MenuActivity implements IProfiler {

private ProgressBar mProgressar;

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

    ActivityProfileBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_profile);
    binding.setUser(new User());
    binding.setViewActions(new ProfileViewModel(this));


    //get the toolbar
    Toolbar tb = (Toolbar)findViewById(R.id.toolbarMain);
    setSupportActionBar(tb);

    //set the Progressbar
    mProgressar = (ProgressBar)findViewById(R.id.progressPostUser);
}

@Override
public void ShowProgressbar() {
    mProgressar.setVisibility(View.VISIBLE);
}

@Override
public void HideProgressbar() {
    mProgressar.setVisibility(View.GONE);
}

@Override
public void MakeToast() {
    Toast.makeText(this, "Some toast", Toast.LENGTH_SHORT);
}
}

My ProfileViewModel, which excepts the interface as parameter:

    public class ProfileViewModel {

    private User mUser;
    private IProfiler mProfiler;

    public ProfileViewModel(IProfiler profiler){
        mProfiler = profiler;
    }

    public void onSaveClicked(User user) {
        try {
            String nameTest = user.get_name();
            String surnameTest = user.get_surname();

            new AsyncTaskPost(mProfiler).execute(new URL("http://www.Trackme.com"));
        }
        catch (Exception ex) {

        }
    }
}

And then finally, my AsyncTaskPost.

public class AsyncTaskPost extends AsyncTask<URL, Void, Void> {

private IProfiler mProfilerActions;

public AsyncTaskPost(IProfiler profilerActions){
    mProfilerActions = profilerActions;
}

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

@Override
protected Void doInBackground(URL... urls) {
    try{
        Thread.sleep(5000);
        return null;
    }
    catch (Exception ex) {
        return null;
    }
}

@Override
protected void onPostExecute(Void aVoid) {
    mProfilerActions.HideProgressbar();
    mProfilerActions.MakeToast();
}

@Override
protected void onCancelled() {

    super.onCancelled();
    mProfilerActions.HideProgressbar();
}
}