Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should my business logic be in the fragment or the activity above?

Tags:

android

I'm trying to use fragments and a list view with an array adapter, and having trouble calling my method from the onClickListener in the array adapter.

If I understand the pattern correctly, a fragment should be self-sufficient, so I want to put my business logic in there. But I can't manage to call it from the array adapter. I can call it if I put it in the main activity, but doesn't that preclude me from using the fragment in another activity and break the paradigm?

Is my business logic in the wrong place, or am I not calling it correctly?

Here is my ArrayAdapter;

public class RecipientsListAdapter extends ArrayAdapter<Recipient>{

    Context context;
    int layoutResourceId;   
    Recipient data[] = null;

    public RecipientsListAdapter(Context context, int layoutResourceId, Recipient[] data) {
        super(context, layoutResourceId, data);
        this.layoutResourceId = layoutResourceId;
        this.context = context;
        this.data = data;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        View row = convertView;
        RecipientHolder holder = null;

        final boolean isLastRow = (position == data.length-1);

        if(row == null)
        {
            LayoutInflater inflater = ((Activity)context).getLayoutInflater();
            row = inflater.inflate(layoutResourceId, parent, false);

            holder = new RecipientHolder();
            holder.imgIcon = (ImageView)row.findViewById(R.id.imgIcon);
            holder.txtTitle = (TextView)row.findViewById(R.id.txtTitle);

            row.setTag(holder);
        }
        else
        {
            holder = (RecipientHolder)row.getTag();
        }

        final Recipient recipient = data[position];
        holder.txtTitle.setText(recipient.displayName);
        holder.imgIcon.setImageResource(recipient.icon);

        row.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                ((MainActivity)context).onChildItemSelected(position);
                if(isLastRow){
                //((RecipientsFragment).getContext()).launchContactPicker();


                    ((MainActivity)context)).launchContactPicker();


                }
                else{
                    Toast.makeText(getContext(), recipient.displayName, Toast.LENGTH_SHORT).show();
                }
            }
        });

        return row;
    }
like image 643
charliefortune Avatar asked Jan 20 '13 18:01

charliefortune


2 Answers

The Adapter should not be what calls any click listeners, and should not be attaching a click listener in the getView() method. Instead, you should be using ListFragment, and simply override onListItemClick() in the fragment. Then you can either dispatch that event to the Activity by invoking a listener interface callback, or handle it directly within the Fragment. You can also use the support library if you want to support Android versions prior to the release of SDK 11.

like image 123
Joe Avatar answered Oct 21 '22 21:10

Joe


Do not upcast the context you are given, this is implicitly coupling the adapter to that particular Activity without announcing it in the constructor signature.

Since you have such a strong coupling, either add RecipientsFragment as parameter to the constructor, or do what Joe suggests and use onListItemClick on the ListView in the Fragment itself. However, there are often legitimate uses for using OnClickListeners in the Adapter (e.g., multiple clickable items), so in those cases you do have to just pass the Fragment itself.

If you find that more than 2 different things will be using that adapter (say, 3 different Fragments), introduce a callback interface and have the Fragments implement it (and pass that interface as the parameter to the constructor).

like image 29
Delyan Avatar answered Oct 21 '22 21:10

Delyan