0

i am making a image downloading android app that takes a certain number of image urls, creates GUI for required for the number of urls provided and then displays them. I succeeded in doing so. Now i am trying to separate a module of this task. In order to do so i want to do everything out of the MainActivity class. The MainActivity class just provides the input in the form of urls and then takes the result back and displays it.

JAVA Code: MainActivity Class:

public class MainActivity extends AppCompatActivity {


    ArrayList<String> urls = new ArrayList<>();

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

        addUrls();
   }

   public void downloadImages(View view){
       Manager manager = new Manager(MainActivity.this,urls);
       manager.downloadImages();
   }

    public void addUrls() {

        urls.add("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTXEZRoYOhIJxL5foNz_NlatDlgYStzZgVIiKuo6vtRtz2wY-8b4Q");
        urls.add("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSFL3WYbqNOX-dwjtT1LroBlY5W-3YuwSIuCMRaLpnjMXbVPEJy");
        urls.add("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQiwgrJeAJN-7lcy92N51uP7XzccK_p-fTSJNCXPLPSVih8wqPf");
        urls.add("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT19dYLCEZlMRqojedJB-05jTrflD74nasvkXs-SdVeyM2BEpCSFA");
        urls.add("http://wallpaperswide.com/download/high_tech_earth-wallpaper-2880x1800.jpg");
        urls.add("https://www.gettyimages.ca/gi-resources/images/Homepage/Hero/UK/CMS_Creative_164657191_Kingfisher.jpg");
        urls.add("https://images.unsplash.com/photo-1418489098061-ce87b5dc3aee?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=2f033882f3c25404e3f904fbfe2351be&w=1000&q=80");
        urls.add("https://techcrunch.com/wp-content/uploads/2018/03/gettyimages-705351545.jpg?w=730&crop=1");

    }
}

Manager class(a class that acts as a manager or GUI making class):

class Manager {

    private Context context;
    private ArrayList<String> urls = new ArrayList<>();

    LinearLayout linearLayoutScrollView;

    ImageView imageView;
    ProgressBar progressBar;
    RelativeLayout relativeLayout;

    ImageDownloader imageDownloader;
    public Manager(Context context, ArrayList<String> urls) {
        this.context = context;
        this.urls = urls;
    }

    public void buildUI() {
        System.out.println("Error # 1");
        imageView = new ImageView(context);
        imageView.setVisibility(View.GONE);

        System.out.println("Error # 2");
        progressBar = new ProgressBar(context);
        progressBar.setIndeterminate(true);
        progressBar.setVisibility(View.GONE);

        System.out.println("Error # 3");
        relativeLayout = new RelativeLayout(context);
        relativeLayout.addView(imageView);
        relativeLayout.addView(progressBar);

        System.out.println("Error # 4");
        linearLayoutScrollView = (LinearLayout) findViewById(R.id.linearLayoutScrollView);
        linearLayoutScrollView.addView(relativeLayout);
        System.out.println("Error # 5");

    }

    public void downloadImages() {
        for (int i = 0; i < urls.size(); i++) {

            buildUI();

            imageDownloader = new ImageDownloader(imageView,progressBar,imageDownloader);

            progressBar.setVisibility(View.VISIBLE);

            imageDownloader.execute(urls.get(i));

        }
    }    
}

ImageDownloader Class:

public class ImageDownloader extends AsyncTask<String, Void, Bitmap> {

    ImageView imageView;
    ProgressBar progressBar;
    ImageDownloader imageDownloader;

    public ImageDownloader(ImageView imageView, ProgressBar progressBar, ImageDownloader imageDownloader) {
        this.imageView = imageView;
        this.progressBar = progressBar;
        this.imageDownloader = imageDownloader;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        imageView.setVisibility(View.GONE);
        progressBar.setVisibility(View.VISIBLE);
    }


    @Override
    protected Bitmap doInBackground(String... params) {
        try {
            URL url = new URL(params[0]);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.connect();
            System.out.println("Content Type = " + connection.getContentType());
            if (connection.getContentType().contains("image")) {
                InputStream inputStream = connection.getInputStream();
                return BitmapFactory.decodeStream(inputStream);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        super.onPostExecute(bitmap);
        try {
            imageView.setImageBitmap(bitmap);
            progressBar.setVisibility(View.GONE);
            imageView.setVisibility(View.VISIBLE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

XML Code:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="match_parent"
    tools:context="com.example.syeddanish.downloadingimages.MainActivity">

    <LinearLayout
        android:id="@+id/buttonsLinearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true">

        <Button
            android:id="@+id/downloadImagesButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="downloadImages"
            android:text="Download Images" />

        <Button
            android:id="@+id/resetButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="reset"
            android:text="Reset" />

    </LinearLayout>

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/buttonsLinearLayout">

        <LinearLayout
            android:id="@+id/linearLayoutScrollView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" />
    </ScrollView>


</RelativeLayout>

The issue is that in the "Manager"class it gives error to "add qualifier to method" in findViewById() method.

linearLayoutScrollView = (LinearLayout) findViewById(R.id.linearLayoutScrollView);

And if i add a this qualifier it changes the above line like this

linearLayoutScrollView = (LinearLayout) linearLayoutScrollView.findViewById(R.id.linearLayoutScrollView);

and then gives the error logcat:

E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.syeddanish.downloadingimages, PID: 29339 java.lang.IllegalStateException: Could not execute method for android:onClick at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:293) at android.view.View.performClick(View.java:6291) at android.view.View$PerformClick.run(View.java:24931) at android.os.Handler.handleCallback(Handler.java:808) at android.os.Handler.dispatchMessage(Handler.java:101) at android.os.Looper.loop(Looper.java:166) at android.app.ActivityThread.main(ActivityThread.java:7425) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921) Caused by: java.lang.reflect.InvocationTargetException at java.lang.reflect.Method.invoke(Native Method) at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288) at android.view.View.performClick(View.java:6291)  at android.view.View$PerformClick.run(View.java:24931)  at android.os.Handler.handleCallback(Handler.java:808)  at android.os.Handler.dispatchMessage(Handler.java:101)  at android.os.Looper.loop(Looper.java:166)  at android.app.ActivityThread.main(ActivityThread.java:7425)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)  Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.widget.LinearLayout.findViewById(int)' on a null object reference at com.example.syeddanish.downloadingimages.Manager.buildUI(Manager.java:45) at com.example.syeddanish.downloadingimages.Manager.downloadImages(Manager.java:54) at com.example.syeddanish.downloadingimages.MainActivity.downloadImages(MainActivity.java:24) at java.lang.reflect.Method.invoke(Native Method)  at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)  at android.view.View.performClick(View.java:6291)  at android.view.View$PerformClick.run(View.java:24931)  at android.os.Handler.handleCallback(Handler.java:808)  at android.os.Handler.dispatchMessage(Handler.java:101)  at android.os.Looper.loop(Looper.java:166)  at android.app.ActivityThread.main(ActivityThread.java:7425)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)

please point out what i am doing wrong and what to change to remove this issue.

Sagar
  • 23,903
  • 4
  • 62
  • 62

2 Answers2

2

findViewById is a method of View according to the official documentation:

Finds the first descendant view with the given ID, the view itself if the ID matches getId(), or null if the ID is invalid (< 0) or there is no matching view in the hierarchy.

or a method of activity:

Finds a view that was identified by the android:id XML attribute that was processed in onCreate(Bundle)

It is simple, It find a view inside a parent view. In most case, findviewById() is called inside an Activity, a Fragment or a CustomView. There, we call:

this.findViewById(ID_OF_THE_CHILD)

So, if you want to retrieve R.id.linearLayoutScrollView you need a reference to the parent view. you can pass it from buildGUI like this:

public void buildUI(View parent) {
...
linearLayoutScrollView = (LinearLayout)parent.findViewById(R.id.linearLayoutScrollView);
}

So, in Activity for example, you will call

buildGUI(findViewById(R.layout.activity_main));
maheryhaja
  • 1,617
  • 11
  • 18
1

the problem solved by putting this in the MainActivity class linearLayoutScrollView = (LinearLayout) findViewById(R.id.linearLayoutScrollView); and then then passing this linearLayoutScrollView as an argument in the the constructor of the "Manager" class.

public Manager(Context context, ArrayList<String> urls,LinearLayout linearLayoutScrollView) {
        this.context = context;
        this.urls = urls;
        this.linearLayoutScrollView = linearLayoutScrollView;
    }

and then making this linearLayoutScrollView as a local member of the "Manager" class and use it as required.

  • nice work, but imagine you add some new elements to your xml file and need to retrieve it to your manager. You will need to add parameters to constructor. – maheryhaja Jul 18 '18 at 06:37
  • @maheryhaja you mean that in this case it will work but to implement other things it will become more complex? – Syed Danish Jul 18 '18 at 06:52
  • yes, keeping adding param to the constructor is not a good practice. However, seeing your code style and effort, I'm confident you will produce realy nice code – maheryhaja Jul 18 '18 at 07:07
  • Thanks for your appreciation and help. People like you willing to help others motivates me – Syed Danish Jul 18 '18 at 07:40