1

I am a beginner in Android development.
I know, that title seems difficult to understand, but please let me explain

My app is a Bus Lines Guide of my city, it contains two distinct Views:

  • A ListView with all bus lines in numerical, simple order, nothing special. It contains exactly 100 items.
  • A NavigationDrawer IN ANOTHER ACTIVITY with these same lines, but separated by zones (North, South, East, Southeast)

Each NavigationDrawer menu leads to an Activity with several buttons and bus lines, but only those belonging to Zone X (East, for example)

Here the layout of ActivityEast:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:paddingTop="6dp"
    android:paddingBottom="6dp"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/activity_leste" tools:context="com.lennoardsilva.teresinabus.Leste">

    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/scrollView2"
        android:layout_alignParentTop="true">

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_245"
                android:id="@+id/botao_l_245" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_401"
                android:id="@+id/botao_l_401" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_402"
                android:id="@+id/botao_l_402" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_403"
                android:id="@+id/botao_l_403" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_404"
                android:id="@+id/botao_l_404" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_405"
                android:id="@+id/botao_l_405" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_501"
                android:id="@+id/botao_l_501" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_502"
                android:id="@+id/botao_l_502" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_503"
                android:id="@+id/botao_l_503" />
            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_512"
                android:id="@+id/botao_l_512" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_513"
                android:id="@+id/botao_l_513" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_518"
                android:id="@+id/botao_l_518" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_520"
                android:id="@+id/botao_l_520" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_521"
                android:id="@+id/botao_l_521" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_522"
                android:id="@+id/botao_l_522" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_523"
                android:id="@+id/botao_l_523" />

            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/l_610"
                android:id="@+id/botao_l_610" />
            <Button
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/carregar_mais"
                android:id="@+id/botao_carregar_leste"
                android:background="@color/cinza_escuro"
                android:textColor="@color/branco"
                android:textStyle="bold"
                android:onClick="leste_2"
                android:visibility="gone" />

        </LinearLayout>
    </ScrollView>
</RelativeLayout>

<!-- As a told you, a lot of buttons -->

Here LineListFragment:

package com.lennoardsilva.teresinabus;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import com.lennoardsilva.teresinabus.dummy.DummyContent;

public class LinhaListFragment extends ListFragment {

    private static final String STATE_ACTIVATED_POSITION = "activated_position";

    private Callbacks mCallbacks = sDummyCallbacks;

    private int mActivatedPosition = ListView.INVALID_POSITION;

    public interface Callbacks {
        public void onItemSelected(String id);
    }

    private static Callbacks sDummyCallbacks = new Callbacks() {
        @Override
        public void onItemSelected(String id) {
        }
    };

    public LinhaListFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setListAdapter(new ArrayAdapter<DummyContent.DummyItem>(getActivity(),android.R.layout.simple_list_item_activated_1, android.R.id.text1, DummyContent.ITEMS));
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (savedInstanceState != null && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
            setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
        }
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        // Activities containing this fragment must implement its callbacks.
        if (!(activity instanceof Callbacks)) {
            throw new IllegalStateException("Activity must implement fragment's callbacks.");
        }
        mCallbacks = (Callbacks) activity;
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mCallbacks = sDummyCallbacks;
    }

    @Override
    public void onListItemClick(ListView listView, View view, int position, long id) {
        super.onListItemClick(listView, view, position, id);

        mCallbacks.onItemSelected(DummyContent.ITEMS.get(position).id);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (mActivatedPosition != ListView.INVALID_POSITION) {
            outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
        }
    }

    public void setActivateOnItemClick(boolean activateOnItemClick) {
        getListView().setChoiceMode(activateOnItemClick ? ListView.CHOICE_MODE_SINGLE : ListView.CHOICE_MODE_NONE);
    }

    private void setActivatedPosition(int position) {
        if (position == ListView.INVALID_POSITION) {
            getListView().setItemChecked(mActivatedPosition, false);
        } else {
            getListView().setItemChecked(position, true);
        }
        mActivatedPosition = position;
    }
}

Here the Line ListActivity:

public class LinhaListActivity extends AppCompatActivity
        implements LinhaListFragment.Callbacks {

    private boolean mTwoPane;

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        toolbar.setTitle(getTitle());

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                DialogFragment dialog = new FragmentoDatabase();
                dialog.show(getFragmentManager(), "FragmentoDatabaseTag");

            }
        });
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        if (findViewById(R.id.linha_detail_container) != null) {
            mTwoPane = true;
            ((LinhaListFragment) getSupportFragmentManager()
                    .findFragmentById(R.id.linha_list))
                    .setActivateOnItemClick(true);
        }

        // TODO: If exposing deep links into your app, handle intents here.
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == android.R.id.home) {
            NavUtils.navigateUpFromSameTask(this);
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onItemSelected(String id) {
        if (mTwoPane) {
            Bundle arguments = new Bundle();
            arguments.putString(LinhaDetailFragment.ARG_ITEM_ID, id);
            LinhaDetailFragment fragment = new LinhaDetailFragment();
            fragment.setArguments(arguments);
            getSupportFragmentManager().beginTransaction().replace(R.id.linha_detail_container, fragment).commit();

        } else {
            Intent detailIntent = new Intent(this, LinhaDetailActivity.class);
            detailIntent.putExtra(LinhaDetailFragment.ARG_ITEM_ID, id);
            startActivity(detailIntent);
        }
    }
}

Here DummyContent:

public class DummyContent {

    public static List<DummyItem> ITEMS = new ArrayList<DummyItem>();

    public static Map<String, DummyItem> ITEM_MAP = new HashMap<String, DummyItem>();

    static {
        addItem(new DummyItem("1", "004 - LINE NAME", "Line 004 Details"));
        addItem(new DummyItem("2", "005 - LINE NAME", "Line 005 Details"));
        addItem(new DummyItem("3", "And so on....", "Details"));
        addItem(new DummyItem("100", "100 - LINE NAME", "Line 100 Details"));
      }

    private static void addItem(DummyItem item) {
        ITEMS.add(item);
        ITEM_MAP.put(item.id, item);
    }
    public static class DummyItem {
        public String id;
        public String content;
        public String details;

        public DummyItem(String id, String content, String details) {
            this.id = id;
            this.content = content;
            this.details = details;
        }

        @Override
        public String toString() {
            return content;
        }
    }
}

Question: How to make one of these buttons start the corresponding ItemDetailsActivity the ListView?

Example: In ActivityEast I have the botao_l_501, in ListView it corresponds to item 33. I want botao_l_501 start the details of item 33 of the ListView. (Template Master / Detail Flow, Google)

Things you might want to know:

  • The ListView is in another Activity, separated from all the items in the Navigation Drawer, but you can start it by a FAB;
  • The ListView is a model of the Master / Detail Flow. The items are already set (Its details not yet);
Lennoard Silva
  • 767
  • 1
  • 8
  • 17

1 Answers1

1

So basically you need to share the adapter's information that is in LinhaListFragment with the zones activities (i.e. ActivityEast). The problem is that in LinhaListFragment you've an Adapter because you're using a ListFragment but in the ActivityEast you've a LinearLayout with a bunch of Buttons, so how we do map those two things to work together?

First solution (Hack & Nasty)

In your DummyContent class that when you're adding the items, add also a reference to the Button id:

static {
    addItem(new DummyItem(R.id.idbotao_l_001, "1", "004 - LINE NAME", "Line 004 Details"));
    addItem(new DummyItem(R.id.idbotao_l_002, "2", "005 - LINE NAME", "Line 005 Details"));
    addItem(new DummyItem(R.id.idbotao_l_033, "33", "033 - LINE NAME", "Details"));
    addItem(new DummyItem(R.id.idbotao_l_100, "100", "100 - LINE NAME", "Line 100 Details"));
  }

Then in the onClick of each button you can do something like:

@Override
public void onClick(View view) {
    for (DummyItem item : DummyContents.ITEMS) {
         if (item.resourceId == view.getId()) {
             //Start the item detail activity
             break;
         }
    }
}

I don't know if you're using a DummyContent class because your content is never going to change of if you're using It because you're just testing the code. If in the future you're going to fetch that data from a webservice then this solution is useless.

Second solution (Clean)

First of all if you really want to follow the master/detail specs then you should use activities only as fragment holders, therefore the zones activities like ActivityEast shouldn't exist neither you should've a LinearLayout with a bunch of buttons. Replace it by a ZoneActivity which contains a ZoneFragment with a RecyclerView (nobody uses ListView or ListFragment anymore) the RecyclerView should be set with the same Adapter that you use in the LinhaListFragment when you start a ZoneActivity It's a requirment that the zone id is send by the bundle of the Intent that id is pass through the ZoneFragment and finally set to the Adapter. The Adapter contains the logic to filter/show the lines per zone. The zone id parameter shouldn't be necessary because the ListFragment It's also going to use the same adapter and doesn't has a zone id.

Since you said that you just started in android development there's some tips that'd help you:

  • Don't use ListView use RecyclerView instead.

  • Never do something like you did in ActivityEast if you're going to have so many buttons use a RecyclerView even if your data is static.

  • Nobody uses things like ListFragment any more. If you extends from something like that you're attach to that class, try to inherith from your own base classes or the very basic base classes like Fragment and Activity.

  • Try to use regions no separate your code. For example:

public class MyFragment extends Fragment {

//region Variables
private int myField;
//endregion

//region Fragment lifecycle
protected void onCreate(Bundle saveInstance) {
 //...
}
//endregion
}

You can then fold them and read the code in a more confortable way.

  • Never start an activity explicity for example:

BAD

Intent intent = new Intent(context, MyActivity.class);
intent.putExtra("zoneId",myZoneId);
startActivity(intent);

GOOD

startActivity(MyActivity.getStartIntent(context, myZoneId));

....
//In MyActivity class
public static getStartIntent(Context context, int zoneId) {
    Intent intent = new Intent(context, MyActivity.class);
    intent.putExtra(MyActiviy.ZONE_ID, zoneId);
    return zoneId;
}

}

4gus71n
  • 3,717
  • 3
  • 39
  • 66
  • Thank you for this helpful answer!!!! There is some many content so I will start searching right now. Still a bit difficult for me but I'm not giving up. About that buttons, that was the first and easy solution I've found to put that together, now I know what I should do. Thanks in advance. Second solution looks pretty good and clean as you said, I will try out – Lennoard Silva Mar 14 '16 at 10:56
  • Glad this answer helped you, if you fill satisfied with the answer please accept it. – 4gus71n Mar 14 '16 at 12:31
  • Ok tzz it helped a lot seriously, if you could explain erm... Noob friendly the second one... I'd be glad. – Lennoard Silva Mar 14 '16 at 14:17
  • Yes, don't worry give me a few minutes and I'll update the answer. – 4gus71n Mar 16 '16 at 14:05
  • Please don't worry about it, i already moved on to RecyclerView. I made a pretty beautiful list but now I'm having problems with OnClickListener, I can't set it individually for each card. None of the Stack Overflow helped me.... – Lennoard Silva Mar 16 '16 at 17:19
  • What you have basically have to do is pass a Callback from your RecyclerView.Adapter to your ViewHolder, I mean in the method onBindViewHolder, when you do the new CustomViewHolder(view,mCallback) you must pass a callback, that callback is also set from your Activity/Fragment to your Adapter. And In the ViewHolder what you do is trigger that callback in the onTouch/onClick event of any of your views. – 4gus71n Mar 16 '16 at 17:37
  • Check [this](http://stackoverflow.com/questions/24471109/recyclerview-onclick/26553536) – 4gus71n Mar 16 '16 at 17:40
  • How can I set a OnClickListener >> New intent >> Start activity FOR EACH OF THE CARDS? – Lennoard Silva Mar 16 '16 at 18:14
  • Here you [have](https://gist.github.com/larghi-truelogic/0b5aa404d8ed61fc2dd1) although isn't a 100% clean implementation there's several ways to achieve It. Instead of using final params you can try to get the children position using the view and that way avoiding the final. But in the end is the same. – 4gus71n Mar 16 '16 at 18:28
  • Keep in mind that I didn't compile that code is just an example – 4gus71n Mar 16 '16 at 18:28
  • Fine, but in that "my clickable element" how to set an "id" to a card element so I can use the OnClickListener? .PS. Do you have any private message method? Cause I still have a lot of questions, sorry for the bothering and thanks for the patience I quote Guns and Roses :v – Lennoard Silva Mar 16 '16 at 22:27