5

I'm using Android Studio. I haven't been able to find an answer online, so even a link to a solution would be helpful.

I have an Activity, which includes a number of Fragments. One of these Fragments is called BookGridFragment, which uses a class called BookGrid.

BookGridFragment looks like this (I've left out irrelevant bits):

public class BookGridFragment extends Fragment {

    BookGrid myBookGrid;

    public BookGridFragment() {}

    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        // Inflate layout
        View rootView = inflater.inflate(
                R.layout.fragment_book_grid, container, false);

        myBookGrid = rootView.findViewById(book_grid);

        return rootView;
    }

    public void setBook(Book thisBook) {
        myBookGrid.setBook(thisBook);
    }
}

The BookGrid class is:

public class BookGrid extends View {

    private Book mBook;

    public BookGrid(Context thisContext, AttributeSet attrs) {
        super(thisContext, attrs);
    }

    public void setBook(Book newBook) {
        mBook = newBook;
    }

    protected void onDraw(Canvas canvas) {

        if (mBook == null) return; 

        canvas.save();

        draw_book_details(); 
        // draw_book_details() is a function which just takes 
        //  the book info and displays it in a grid

        canvas.restore();
    }

   public boolean onTouchEvent(MotionEvent event) {

       // This function responds to the user tapping a piece of
       //  book info within the grid

       // THIS IS WHERE I'M HAVING PROBLEMS
   }
}

So, that all works fine. The issue is, that I need the BookGridFragment to know when the user touches the BookGrid and to pass that information to another Fragment (via the Activity). So, I assume that when the onTouchEvent is reached, that should somehow notify the BookGridFragment that the BookGrid was touched, but I can't figure out how to do that.

Everything I've found online is about passing information between Fragments, but that approach doesn't work here as the BookGrid class doesn't "know" that it's within a BookGridFragment.

halfer
  • 19,824
  • 17
  • 99
  • 186
Sharon
  • 3,471
  • 13
  • 60
  • 93

5 Answers5

4

You could use the same idea that is used to communicate a Fragment and an Activity. Create an interface:

public interface OnBookGridTouched{
    void onTouchGrid();
} 

Add a variable to your BookGrid:

private OnBookGridTouched mCallback;

Add a setter to this variable:

public void setCallback(OnBookGridTouched callback){
    mCallback = callback;
}

Then make your fragment implement the interface:

public class BookGridFragment extends Fragment implements OnBookGridTouched  {

You'll be forced to implement the method onTouchGrid

In your fragment onCreateView pass the fragment to your custom view:

myBookGrid.setCallback(this);

Finally, in your custom view you can call the callback to reference the fragment:

 public boolean onTouchEvent(MotionEvent event) {

       // This function responds to the user tapping a piece of
       //  book info within the grid

       // THIS IS WHERE I'M HAVING PROBLEMS
       mCallback.onTouchGrid();
   }
Levi Moreira
  • 11,917
  • 4
  • 32
  • 46
  • Thank you - this worked perfectly. Made me a bit more confident at creating interfaces too - I've seen them before, but stayed away. Now I see they're not too complicated. – Sharon Jun 20 '18 at 20:19
  • 1
    Awn that's great I could help you in both ways :) you're welcome, I was glad I could help! – Levi Moreira Jun 20 '18 at 20:22
1

A solution could be to set the onTouch/onClick listener in the fragment instead of in the BookGrid itself. From there you can use the fragment method getActivity() to call an activity method, parsing on the correct data to the correct fragment.

Lars
  • 710
  • 5
  • 19
1

I think this situation is very similar to a Fragment containing a Button.

The Button has a method which accepts something implementing a certain interface (for the Button: View.OnClickListener). The Fragment calls that method (for the Button: setOnClickListener()) to pass in the desired Object implementing all the required methods, either an anonymous class or maybe a field or the Fragment itself. There are pros and cons for all three approaches, it depends on your situation which one is best.

They have in common that BookGrid should have an interface as well as a method so other classes can set the current Object implementing that interface.

Bö macht Blau
  • 12,820
  • 5
  • 40
  • 61
1

I am not quite sure about the exact scenario that you are having there. However, if the problem is the communication between the fragment and an activity which hosts the fragment, then you might think of the following implementation.

Let me point out some of your concerns first.

Everything I've found online is about passing information between Fragments, but that approach doesn't work here as the BookGrid class doesn't "know" that it's within a BookGridFragment.

BookGrid class will know the context of its existence when you will pass the Context towards it while calling a function of it. So I would like to suggest passing the context of the Activity or Fragment when you are calling a function from your BookGrid class.

public class BookGrid extends View {

    private Book mBook;
    private Context context;

    public BookGrid(Context thisContext, AttributeSet attrs) {
        super(thisContext, attrs);
        this.context = thisContext;
    }

    public void setBook(Book newBook) {
        mBook = newBook;
    }

    protected void onDraw(Canvas canvas) {

        if (mBook == null) return; 

        canvas.save();

        draw_book_details(); 
        // draw_book_details() is a function which just takes 
        //  the book info and displays it in a grid

        canvas.restore();
    }

   public boolean onTouchEvent(MotionEvent event) {
       // Call the function of your host activity
       ((YourActivity)(thisContext)).onBookGridTouched();
   }
}

Now write a public method in your activity class which hosts the fragment named onBookGridTouched.

public void onBookGridTouched() {
    // Communicate with other fragments here
}

However, a noble approach of solving this problem in a more generic way is to use an interface and then implement the interface wherever necessary like @LeviAlbuquerque suggested.

I am just putting another workaround which is a bit static.

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
1

Assuming that you have ONE Activity responsible of all fragments:

1.Create an interface in your BookGrid:

public interface ActionHappened {
        void onActionHappened();
    }

2.Create an instance of your interface within your BookGrid class and trigger the method onActionHappened where you want it to be triggered. For instance, if you would like it to happen in your onDraw(), then do the following:

ActionHappened actionHappened;

protected void onDraw(Canvas canvas) {

        if (mBook == null) return; 

        canvas.save();

        draw_book_details(); 
        // draw_book_details() is a function which just takes 
        //  the book info and displays it in a grid

        canvas.restore();

        actionHappened.onActionHappened();
    }

3.Implement your interface within your activity

public class ActivityA extends AppCompatActivity implements BookGrid.ActionHappened {}

4.Within your implemented method, trigger the method:

@Override
public void onActionHappened() {
    Fragment fragmentA = getSupportFragmentManager().findFragmentByTag(R.id.fragmentA);
    Fragment fragmentB = getSupportFragmentManager().findFragmentByTag(R.id.fragmentB);

    //Trigger that method from your activity to fragmentA or fragmentB
    fragmentA.doWork();
    fragmentB.doWork();
}

Wether you would like to pass data to fragmentA or fragmentB, doWork() method will do that for you. Make you create such a method in the corresponding fragment.

Red M
  • 2,609
  • 3
  • 30
  • 50