Wie kann ich eine View(bestehend aus einer Bitmap) in einem Fragment mit dem Fragment kommunizieren lassen ?

  • Antworten:9
Mathias Wittig
  • Forum-Beiträge: 24

24.07.2015, 14:51:29 via Website

Ich hab ne Activity mit einem Fragment und darin einer View bestehend aus einer Bitmap. Von Fragment kann ich super mittels Interface zur Activity kommunizieren. Von View also der Bitmap zum Fragment bzw. auch zur Activity bekomme ich eine Nullpointerexeption "cannot invoke interface method void... on a null object reference"

Ich will nämlich ein Karte(als Bitmap )
anzeigen und wenn auf sie geklickt wurde soll ein neues Fragment oder eine neue Activity erscheinen. Das klicken an sich wird via onTouchEvent in der Klasse mit der Bitmap geregelt und funktioniert auch.

Woran kann es liegen, dass ich von der View Klasse nicht zum Fragment kommunizieren kann ??

Vielen Dank für euere Antworten bereits im voraus !!

— geändert am 24.07.2015, 17:38:51

Antworten
Pascal P.
  • Admin
  • Forum-Beiträge: 11.286

25.07.2015, 07:18:40 via App

Wenn du eine Fragmentinstanz hast, sollte das schon gehen. Zeig mal deinen Code

— geändert am 25.07.2015, 07:18:53

LG Pascal //It's not a bug, it's a feature. :) ;)

Antworten
Mathias Wittig
  • Forum-Beiträge: 24

25.07.2015, 12:10:35 via Website

Das ist mein Code:
Die View-Klasse beinhaltet die Bitmap und kann bereits registrieren ob auf die Bitmap geklickt wurde oder daneben.
(Ich kann da leider keine Imageview hernehmen, da ich später lauter einzelne Bundesländer als gesamte Karte zusamen anzeigen will, und man auch ein bestimmtes Bundesland klicken können soll)

package com.mwittig98gmail.derberater;

import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;

import java.text.DecimalFormat;
import java.util.logging.LogRecord;

/**
* Created by Mathias on 18.07.2015.
*/
public class Karte extends View {

Bitmap land;
Touch mOnTouched;

public Karte(Context context) {
    super(context);
    land = BitmapFactory.decodeResource(getResources(), R.drawable.karte_test);
}


@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawColor(Color.WHITE);
    canvas.drawBitmap(land, 0, 260, null);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    //return super.onTouchEvent(event);
    int x = (int) event.getX();
    int y = (int) event.getY();

    if (land.getPixel(x, y-260) != 0) {
        mOnTouched.touched(1);
        return true;

    } else {
        mOnTouched.touched(0);
        return false;
    }
}

public interface Touch{
    void touched(int i);
}

}

Das Fragment zu dem kommuniziert werde soll.
Von dort wird dann der Aufruf gleich über das bereits dort funktionierende Interface OnFragmentInteractionListener zur Activity
weitergeleitet (Ich könnte ja eigentlich auch von der View-Klasse direkt zur Activity weiterleiten, aber ich habs erst mal aufgeteilt):

package com.mwittig98gmail.derberater;

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;

/**
* A simple {@link Fragment} subclass.
* Activities that contain this fragment must implement the
* {@link KarteFragment.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {@link KarteFragment#newInstance} factory method to
* create an instance of this fragment.
*/

public class KarteFragment extends Fragment implements View.OnClickListener,Karte.Touch{

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";

// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;

//Interface
OnFragmentInteractionListener mListener;
//View fuer OnClick()
View view;
//Grafische Karte
public Karte karte;


/**
 * Use this factory method to create a new instance of
 * this fragment using the provided parameters.
 *
 * @param param1 Parameter 1.
 * @param param2 Parameter 2.
 * @return A new instance of fragment KarteFragment.
 */
// TODO: Rename and change types and number of parameters
public static KarteFragment newInstance(String param1, String param2) {
    KarteFragment fragment = new KarteFragment();
    Bundle args = new Bundle();
    args.putString(ARG_PARAM1, param1);
    args.putString(ARG_PARAM2, param2);
    fragment.setArguments(args);
    return fragment;
}

public KarteFragment() {
    // Required empty public constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        mParam1 = getArguments().getString(ARG_PARAM1);
        mParam2 = getArguments().getString(ARG_PARAM2);
    }

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    view = inflater.inflate(R.layout.fragment_karte, container, false);
    view.findViewById(R.id.groud0).setOnClickListener(this);
    karte = new Karte(getActivity());
    FrameLayout frameLayout = (FrameLayout)view.findViewById(R.id.groud0);
    frameLayout.addView(karte);


    return view;
}



// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
    if (mListener != null) {
        mListener.onFragmentInteraction(1);
    }

}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnFragmentInteractionListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement OnFragmentInteractionListener");
    }
}

@Override
public void onDetach() {
    super.onDetach();
    mListener = null;
}

@Override
public void onClick(View v) {
    if(v==view.findViewById(R.id.groud0)) {
        mListener.onFragmentInteraction(1);
    }
}

@Override
public void touched(int i) {
    mListener.onFragmentInteraction(i);
}

/**
 * This interface must be implemented by activities that contain this
 * fragment to allow an interaction in this fragment to be communicated
 * to the activity and potentially other fragments contained in that
 * activity.
 * <p/>
 * See the Android Training lesson <a href=
 * 
 * >Communicating with Other Fragments</a> for more information.
 */



public interface OnFragmentInteractionListener {
    // TODO: Update argument type and name
    void onFragmentInteraction(int i);
}

}

— geändert am 25.07.2015, 16:26:47

Antworten
Mathias Wittig
  • Forum-Beiträge: 24

02.08.2015, 20:12:13 via Website

OK, ich hab' jetzt selbst mal herumgewerkelt und es funktioniert endlich.
Das Interface in der View-Klasse, die die Bitmap beinhaltet darf dort anscheinend nicht sein bzw. "zeigt" auf nichts (NullpointerExeption).
Ich hab es wie folgt gelöst, für welche, die das selbe/ein ähnliches Problem haben wie ich:

Ich hab einen OnTouchListener in meinem Fragment implementiert. Dieser sieht dann in der View-Klasse nach, ob die Kooridaten mit denen der Bitmap übereinstimmen und erhält dann 1 für "ja" und 0 für "nein" zurück(Methode: int woClick()). Dann gehts im Fragment ganz normal über ein Interface weiter.

Der Code:
View-Klasse:

package com.mwittig98gmail.derberater.BitmapViews;

import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;

import com.mwittig98gmail.derberater.R;

import java.text.DecimalFormat;
import java.util.logging.LogRecord;

/**
* Created by Mathias on 18.07.2015.
*/
public class Karte extends View {

Bitmap land;
Touch mOnTouched;
int touched = 0;

public Karte(Context context) {
    super(context);
    land = BitmapFactory.decodeResource(getResources(), R.drawable.karte_test);
}


@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawColor(Color.WHITE);
    canvas.drawBitmap(land, 0, 260, null);
}



public int woClick(int x,int y){
    int wo = 0;
    y=y-260;

    if(y<land.getHeight()&&y>=0) {
        if (land.getPixel(x, y) != 0) {
            wo = 1;
        } else {
            wo = 0;
        }
    }

    return wo;
}

public interface Touch{
    void touched(int i);
}

}

Die Fragment-Klasse

package com.mwittig98gmail.derberater.Fragments;

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;

import com.mwittig98gmail.derberater.BitmapViews.Karte;
import com.mwittig98gmail.derberater.R;

/**
* A simple {@link Fragment} subclass.
* Activities that contain this fragment must implement the
* {@link KarteFragment.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {@link KarteFragment#newInstance} factory method to
* create an instance of this fragment.
*/

public class KarteFragment extends Fragment {

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";

// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;

//Interface
OnFragmentInteractionListener mListener;
//View fuer OnClick()
View view;
//Grafische Karte
public Karte karte;


/**
 * Use this factory method to create a new instance of
 * this fragment using the provided parameters.
 *
 * @param param1 Parameter 1.
 * @param param2 Parameter 2.
 * @return A new instance of fragment KarteFragment.
 */
// TODO: Rename and change types and number of parameters
public static KarteFragment newInstance(String param1, String param2) {
    KarteFragment fragment = new KarteFragment();
    Bundle args = new Bundle();
    args.putString(ARG_PARAM1, param1);
    args.putString(ARG_PARAM2, param2);
    fragment.setArguments(args);
    return fragment;
}

public KarteFragment() {
    // Required empty public constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        mParam1 = getArguments().getString(ARG_PARAM1);
        mParam2 = getArguments().getString(ARG_PARAM2);
    }

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    view = inflater.inflate(R.layout.fragment_karte, container, false);
    //view.findViewById(R.id.groud0);
    karte = new Karte(getActivity());
    FrameLayout frameLayout = (FrameLayout)view.findViewById(R.id.groud0);
    frameLayout.addView(karte);
    String s = "Karte";
    // mListener.ueberschrift(s);
   /*
    Button b1 = (Button)view.findViewById(R.id.i1);
    b1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mListener.onFragmentInteraction(1);
            mListener.ueberschrift(1+"");

        }
    });

    //
    Button b2 = (Button)view.findViewById(R.id.i2);
    b2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mListener.onFragmentInteraction(2);
            mListener.ueberschrift(2+"");

        }
    });
    //
    Button b3 = (Button)view.findViewById(R.id.i3);
    b3.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mListener.onFragmentInteraction(3);
            mListener.ueberschrift(3+"");

        }
    });
    //
    Button b4 = (Button)view.findViewById(R.id.i4);
    b4.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mListener.onFragmentInteraction(4);
            mListener.ueberschrift(4+"");

        }
    });
    //
    Button b5 = (Button)view.findViewById(R.id.i5);
    b5.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mListener.onFragmentInteraction(5);
            mListener.ueberschrift(5+"");

        }
    });
    //
    Button b6 = (Button)view.findViewById(R.id.i6);
    b6.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mListener.onFragmentInteraction(6);
            mListener.ueberschrift(6+"");

        }
    });
    //
    Button b7 = (Button)view.findViewById(R.id.i7);
    b7.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mListener.onFragmentInteraction(7);
            mListener.ueberschrift(7+"");

        }
    });
    //
    Button b8 = (Button)view.findViewById(R.id.i8);
    b8.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mListener.onFragmentInteraction(8);
            mListener.ueberschrift(8 +"");

        }
    });

*/
//view = view.findViewById(R.id.groud0);
// View v = new Karte(getActivity());
karte.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
int click = 0;

            click = karte.woClick(x,y);
            //touched = 0;

            mListener.onFragmentInteraction(click);


            return true;
        }
    });


    return view;
}



// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
    if (mListener != null) {
        mListener.onFragmentInteraction(0);
    }

}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnFragmentInteractionListener) activity;

    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement OnFragmentInteractionListener");
    }
}

@Override
public void onDetach() {
    super.onDetach();
    mListener = null;
}





/**
 * This interface must be implemented by activities that contain this
 * fragment to allow an interaction in this fragment to be communicated
 * to the activity and potentially other fragments contained in that
 * activity.
 * <p/>
 * See the Android Training lesson <a href=
 * >Communicating with Other Fragments</a> for more information.
 */



public interface OnFragmentInteractionListener {
    // TODO: Update argument type and name
    void onFragmentInteraction(int i);
    void ueberschrift(String s);
}

}

Antworten
Sven R.
  • Forum-Beiträge: 1.904

03.08.2015, 07:35:47 via App

Wie wäre es, wie in anderen Views, ein eigenes

setOnLandClickListener()

zu machen? Ist mir jetzt als erstes eingefallen, weil das ja eigentlich die Standardlösung ist. Oder hattest du damit Probleme?

Wenn dir mein Beitrag gefällt, kannst dich einfach mit dem 👍 "Danke"-Button auf der Website dieses Forums bedanken. 😀

Why Java? - Because I can't C#

Antworten
Mathias Wittig
  • Forum-Beiträge: 24

03.08.2015, 10:20:09 via Website

Meinst du in KarteFragment (so was wie karte.setOnClicklistenrr(). ?) oder in Karte (und da dann iwie auf das bitmap)?

Wenn du einen normalen OnClicklistener meinst in KarteFragment, die Karte ist eine Landkarte und unregelmäßig geformt und ich habs mal ausprobiert ,aber da hat es auch clicks daneben registriert(eine View ist ja durchsichtig)

Und in der ViewKlasse hat nicht funktioniert da da kein interface möglich war.Nullpointerexeption

Antworten
Sven R.
  • Forum-Beiträge: 1.904

03.08.2015, 14:13:35 via App

Wenn du in dem View die onTouch überschreibst, danach woClick() aufrufst und dann den Listener(von setKarteIistener) Bescheid gibst, sollte das gehen. Finde ich elegant.

Wenn dir mein Beitrag gefällt, kannst dich einfach mit dem 👍 "Danke"-Button auf der Website dieses Forums bedanken. 😀

Why Java? - Because I can't C#

Antworten
Gelöschter Account
  • Forum-Beiträge: 438

09.08.2015, 06:23:45 via Website

Auch hier möchte ich eine Lanze für einen Eventbus brechen (z.B. Otto, es gibt aber auch andere).
Die verhindern - so lange man Registrierung und Deregistrierung der Listener/Subscriber sauber in den Lifecycle Methoden macht - sehr gut Ressourceleaks, da die Komponenten dann keine Referenzen mehr aufeinander halten.

Aktuelles Entwicklungsprojekt: (thinking) Sudoku Dojo Free (lightbulb)
Ich freue mich über Tester/innen.

Antworten
Sven R.
  • Forum-Beiträge: 1.904

09.08.2015, 11:27:12 via App

@Zielke: Meinst du damit, die Referenzen auf die einzelnen Interfaces(Listener) sind schon zu viel? Oder verstehe ich dich falsch?!

Edit: Anscheined ist das Inteface wirklich schon zu viel; Otto schaffts ja ganz ohne (sichtbare) Referenz, außer natürlich das Singleton. Ich glaube ich werde Otto bald benutzen, sieht sehr schön aus. Danke für den Tipp!

— geändert am 09.08.2015, 18:52:15

Wenn dir mein Beitrag gefällt, kannst dich einfach mit dem 👍 "Danke"-Button auf der Website dieses Forums bedanken. 😀

Why Java? - Because I can't C#

Antworten
Gelöschter Account
  • Forum-Beiträge: 438

09.08.2015, 18:42:28 via Website

Nun ja - das Problem ist m.E. nach, dass Listener-Interfaces eine Entkopplung aus Architektur-Sicht darstellen, aber nicht aus Sicht der Ressourcen.
Damit meine ich folgendes:
Die View, die den Listener ruft, kennt nicht die konkrete Implementierung, die Komponente wird entkoppelt, da sie nun leicht austauschbar ist (SW Architektur).
Allerdings hält die View nun eine Referenz auf den Listener - häufig also auf eine Activity. Das Potential für ein Ressourceleak ist da.

Otto ist natürlich auch kein Silver Bullet - hier hält der Bus Referenzen auf die Subscriber. Nur ist die korrekte Behandlung des Lifecycles in meinen Augen einfacher, da man den Subscriber in den Lifecycle Methoden registriert bzw deregistriert - immer und immer genau an einer einzigen Komponente - dem Bus. So entstehen erst gar keine komplexen Objekt-Graphen, die das Risiko von Ressourceleaks erhöhen.

Aktuelles Entwicklungsprojekt: (thinking) Sudoku Dojo Free (lightbulb)
Ich freue mich über Tester/innen.

Antworten