I'm currently trying to create a 3d game in android and I'm struggling with some issues.
At some point after clicking on a direction button in my game, I try to access an ArrayList but it seems that my ArrayList(that was filled earlier) is now empty.
Is that a threading issue?
#Override
public void Update(ArrayList<ArrayList<IModel>> Database) {
this.DataBase = Database;
System.out.println("---> " + this.DataBase.get(0)); //WORKING
}
Button up_btn = (Button) findViewById(R.joystick.up);
up_btn.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
System.out.println("--------->" + DataBase.get(0)); //NOT WORKING
}
});
here's my whole code ;
public class PlayerController extends Activity implements IController
{
//Properties
private ArrayList<ArrayList<IModel>> DataBase = null;
private GLSurfaceView mGLView;
//Virtual
#Override public void Update(ArrayList<ArrayList<IModel>> Database)
{
this.DataBase = Database;
System.out.println("---> " + this.DataBase.get(0)); //WORKING
}
#Override protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.mGLView = new MySurfaceView(this);
LayoutInflater inflater = getLayoutInflater();
this.getWindow().addContentView(this.mGLView, new ViewGroup
.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT));
this.getWindow().addContentView(inflater.inflate(R.layout.footer, null), new ViewGroup
.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT));
this.getWindow().addContentView(inflater.inflate(R.layout.play, null), new ViewGroup
.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, (int)80));
//Joystick buttons
Button up_btn = (Button) findViewById(R.joystick.up);
up_btn.setOnClickListener(new View.OnClickListener()
{
#Override public void onClick(View v)
{
System.out.println(">>>>>" + DataBase.get(0)); //NOT WORKING..
//UpMotionListener();
}
});
}
#Override protected void onPause()
{
super.onPause();
mGLView.onPause();
}
#Override protected void onResume()
{
super.onResume();
mGLView.onResume();
}
public void UpMotionListener()
{
//System.out.println(">>>>>>>>>" + DataBase.get(0)); //NOT WORKING
}
this in the second case is the instance of the OnClickListener, so you need to fully qualify it, or remove this at all, e.g.:
EnclosingClass.this.dataBase.get(0);
or
dataBase.get(0);
Please note that I started the variable name with lower case, to match Java's naming conventions.
Well, really your problem is that you don't understand the Java language, however that is fixable. Here is a quick overview of the specific knowledge you are missing: http://www.oursland.net/tutorials/java/innerclasses/
Now, to answer your question directly:
Try referring to Database without the "this." in front of it.
this.DataBase.get(0) in your new View.OnClickListener() is referring to your anonymous inner class created when you said new OnClickListener, not the instance class where you created you Database member.
When you use "this" you are saying this class and since this is inside your listener it is referring to the class created inline and the "Database" member variable is out of scope.
Could it be that something else is modifying/clearing the ArrayList in-between the time Update and onClick are called?
Related
I programmed a Vocabulary Trainer with Vocabulary Cards. The Vocabulary Cards are Entries in a Room Database created from an asset. I am displaying these Vocabulary Cards with ViewPager2 in an Activity. I have a 'correct' and a 'false' button and when the user clicks on either, I want to update the Vocabulary Card (-> The entry in the sqlite database) and automatically swipe to the next item of the ViewPager2.
If I implement the buttons in the ViewPager2Adapter, I can't find a way to change the position of the ViewPager2. If I implement the buttons in the activity the sqlite entry does not update properly (After it updates the entry, the activity is constantly refreshed, it seems like it never the leaves the OnClick methode of the button).
So is it possible to change the position of ViewPager2 from inside the ViewPager2Adpater?
Thanks for your help!
That is the relevant code if I have the buttons in my ViewPager2Adapter. Here I don't know how to change the position of the ViewPager2
public void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position) {
VocabularyCard vocabularyCard = currentCards.get(position);
holder.btn_correct.setOnClickListener(view -> {
vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
});
holder.btn_false.setOnClickListener(v15 -> {
vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
});
That is the relevant code if I have the buttons in the Activity. Here the update function triggers an infinite updating of the Activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initAll();
btn_correct_2.setOnClickListener(view -> {
int currentPos = viewpager2.getCurrentItem();
vocabularyViewModel.getCurrentCards().observe(this, vocabularyCards -> {
if (vocabularyCards.size() == currentPos){
Intent intent = new Intent(TestActivity.this, MainActivity.class);
startActivity(intent);
}else {
viewpager2.setCurrentItem(currentPos + 1);
}
VocabularyCard vocabularyCard = vocabularyCards.get(currentPos);
vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
});
});
btn_false_2.setOnClickListener(view -> {
int currentPos = viewpager2.getCurrentItem();
vocabularyViewModel.getCurrentCards().observe(this, vocabularyCards -> {
if (vocabularyCards.size() == currentPos){
Intent intent = new Intent(TestActivity.this, MainActivity.class);
startActivity(intent);
}else {
viewpager2.setCurrentItem(currentPos + 1);
}
VocabularyCard vocabularyCard = vocabularyCards.get(currentPos);
vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
});
});
Objects.requireNonNull(getSupportActionBar()).setTitle(getResources().getString(R.string.learn_new_words));
LiveData<List<VocabularyCard>> allNewCards = vocabularyViewModel.getAllNewCards(goal);
allNewCards.observe(this, vocabularyCards -> vocabularyViewModel.setCurrentCards(vocabularyCards));
vocabularyViewModel.getCurrentCards().observe(this, vocabularyCards -> {
viewPager2Adapter.setCurrentCards(vocabularyCards);
viewpager2.setAdapter(viewPager2Adapter);
viewpager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
#Override
public void onPageSelected(int position) {
super.onPageSelected(position);
}
#Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
}
});
});
The update function in the Room DAO is straightforward:
#Update
void updateSingleVocabularyCard(VocabularyCard vocabularyCard);
I left out all the code that is not relevant.
There are several ways to propagate an event from the adapter to the activity where you manage your cards using ViewPager2. Let's have a look how it can be done either using an interface or using the same view model. But in any case I strongly recommend you to update your database in a background thread to prevent any possible UI lags.
1. Using an interface
This option is more flexible since you can propagate events as well as pass data as parameters. You can also reuse this interface for other cases. As far as I See you have a holder that has 2 buttons for the users to make choices. So our event here would be something like ChoiceEventListener, let's call this interface like so. Then you'd have to add a method to handle this event from within anywhere you wanna hear this event, and let's call its handle method onChoice(). Finally we would need a variable to indicate what the choice is. Now that ready to implement, let's write the new interface...
ChoiceEventListener.java
public interface ChoiceEventListener {
void onChoice(VocabularyCard vocabularyCard, boolean choice);
}
The next thing to do is to implement this interface where you want to listen to this event. In this case it is in your activity. There are 2 ways to do this:
You make your activity to inherit its methods using the implements keyword
YourActivity.java
public class YourActivity extends AppCompatActivity implements ChoiceEventListener {
// Use a background thread for database operations
private Executor executor = Executors.newSingleThreadExecutor();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initAll();
// You must construct your adapter class with the listener
ViewPager2Adapter adapter = new ViewPager2Adapter(/* Other params... */, this);
}
#Override
public void onChoice(VocabularyCard vocabularyCard, boolean choice) {
if(choice) {
// User pressed the correct button
}
else {
// User pressed the false button
}
// Update card in the background
executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
}
}
You can implement it as an anonymous function
YourActivity.java
public class YourActivity extends AppCompatActivity {
// Use a background thread for database operations
private Executor executor = Executors.newSingleThreadExecutor();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initAll();
// You must construct your adapter class with the listener
ViewPager2Adapter adapter = new ViewPager2Adapter(/* Other params... */, (vocabularyCard, choice) -> {
if(choice) {
// User pressed the correct button
}
else {
// User pressed the false button
}
// Update card in the background
executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
});
}
}
Finally the ViewPager2Adapter class implementation would be something like this:
ViewPager2Adapter.java
public class ViewPager2Adapter extends RecyclerView.Adapter<ViewPager2ViewHolder> {
// Here is your listener to deliver the choice event to it
private final ChoiceEventListener listener;
// Constructor
public ViewPager2Adapter(/* Other params... */, ChoiceEventListener listener) {
/* Other inits */
this.listener = listener;
}
public void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position) {
VocabularyCard vocabularyCard = currentCards.get(position);
holder.btn_correct.setOnClickListener(view -> {
listener.onChoice(vocabularyCard, true); // true for correct
});
holder.btn_false.setOnClickListener(v15 -> {
listener.onChoice(vocabularyCard, false); // false for false :)
});
}
}
2. Use the ViewModel for inter-communication
In this option we use a LiveData object to make page switching. The only thing you need to know in your activity is the current position which you get it from the adapter class. Once you update it in the adapter, set the current position value in live data so that you can switch the page in your activity.
VocabularyViewModel.java
public class VocabularyViewModel extends ViewModel {
public MutableLiveData<Integer> mldCurrentPosition = new MutableLiveData<>(0);
}
YourActivity.java
public class YourActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initAll();
vocabularyViewModel.mldCurrentPosition().observe(this, currentPosition -> {
if(currenPosition == null) return; // ignore when null
viewpager2.setCurrentItem(currentPosition + 1);
}
}
}
Finally the ViewPager2Adapter class implementation would be something like this:
ViewPager2Adapter.java
public class ViewPager2Adapter extends RecyclerView.Adapter<ViewPager2ViewHolder> {
// Use a background thread for database operations
private Executor executor = Executors.newSingleThreadExecutor();
public void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position) {
VocabularyCard vocabularyCard = currentCards.get(position);
holder.btn_correct.setOnClickListener(view -> {
// Update card in the background
executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
// Then invoke switching to the next card
vocabularyViewModel.mldCurrentPosition.setValue(position + 1);
});
holder.btn_false.setOnClickListener(v15 -> {
// Update card in the background
executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
// Then invoke switching to the next card
vocabularyViewModel.mldCurrentPosition.setValue(position + 1);
});
}
}
At first my main problem was at how to call a method from the same class, even tough I think I found a way to do this, it's not working as I expected, and I would like to know what would be the best approach to my case.
This is the code I'm working on:
public class EscolhaAtendimento extends AppCompatActivity {
private ViewPager mSlideViewPager;
private LinearLayout mDotLayout;
String TAG = "TasksSample";
private TextView[] mDots;
private SliderAdapter sliderAdapter;
Dialog myDialog;
#Override
public void onCreate (Bundle SavedInstanceState){
super.onCreate(SavedInstanceState);
setContentView(R.layout.escolha_atendimento);
mSlideViewPager = findViewById(R.id.slideViewPager);
mDotLayout = findViewById(R.id.dotsLayout);
sliderAdapter = new SliderAdapter(this);
mSlideViewPager.setAdapter(sliderAdapter);
addDotsIndicator(0);
mSlideViewPager.addOnPageChangeListener(viewListener);
myDialog = new Dialog(this);
}
public void addDotsIndicator(int position){
mDots = new TextView[8];
mDotLayout.removeAllViews();
for (int i= 0; i < mDots.length; i++){
mDots[i] = new TextView(this);
mDots[i].setText(Html.fromHtml("•"));
mDots[i].setTextSize(35);
mDots[i].setTextColor(getResources().getColor(R.color.colorTransparentWhite));
mDotLayout.addView(mDots[i]);
}
if (mDots.length > 0){
mDots[position].setTextColor(getResources().getColor(R.color.colorWhite));
}
}
ViewPager.OnPageChangeListener viewListener = new ViewPager.OnPageChangeListener(){
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected (int i) {
switch (i) {
case 0: {
myDialog.show();
}
addDotsIndicator(i);
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
};
public void ShowPopup(View v) {
TextView txtclose;
//Button btnFollow;
myDialog.setContentView(R.layout.pop_upfinal);
txtclose = myDialog.findViewById(R.id.txtclose);
txtclose.setText("X");
//btnFollow = (Button) myDialog.findViewById(R.id.btnfollow);
txtclose.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myDialog.dismiss();
}
});
myDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
myDialog.show();
}
}
This class is an activity that on user swipe, the text and image from the buttons will change, even though their IDs will stay the same. (That's controlled by another class, it's working well).
Now, I wanted the image button on the activity do something different depending on which page is selected, and that's why there is a initial switch on the onPageSelected method, inside the Page change listener. The image button in the layout has the android:onClick="ShowPopup" tag, and I guess that also complicates things for me, if I wanted it to do something different in that same activity? Also, calling it that way on the switch, every time I change pages, and go back to the first one the popup window will open, since my call is explicit there. (As I said, even tough I found a way to somehow call my method, or at least it's result, it's not working as I expected).
Edit
I tried then changing it like this, so that the button wouldn't rely on the android:onClick="ShowPopup" Tag, and also wouldn't need to call a void method directly on the switch:
Added
public ImageButton popupchoice;
And also this to onCreate method:
popupchoice = this.findViewById(R.id.imgslide1);
Inside the switch I called it like this to get the button ID:
popupchoice.setOnClickListener(image1);
And set the View.OnClickListener like this:
View.OnClickListener image1 = new View.OnClickListener() {
public void onClick(View v) {
TextView txtclose;
//Button btnFollow;
myDialog.setContentView(R.layout.pop_upfinal);
txtclose = myDialog.findViewById(R.id.txtclose);
txtclose.setText("X");
//btnFollow = (Button) myDialog.findViewById(R.id.btnfollow);
txtclose.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myDialog.dismiss();
}
});
myDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
myDialog.show();
}
};
But that returns me:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.setOnClickListener(android.view.View$OnClickListener)' on a null object reference at .EscolhaAtendimento$1.onPageSelected(EscolhaAtendimento.java:81) Line 81 is the one inside the switch with the popupchoice.setOnClickListener(image1).
This error happens on page change, when coming back to the first Page, and also the button click won't work anymore.
I think you can use
EscolhaAtendimento.this.ShowPopup from inside your switch.
I have Preference class extent PreferenceActivity.
I create public static String quality; in Preference.class i add in onCreate
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref);
quality = "QUALITY_HIGH";//initialize
}
and add in Preference.class this method
public void getQuality() {
if (keyquality.equals("480p")) {
quality = "QUALITY_LOW";
//
}
if (keyquality.equals("720p")) {
//
quality = "QUALITY_720P";
}
if (keyquality.equals("1080p")) {
//
quality = "QUALITY_HIGH";
}
}
in another class i create method to get my variable and set settings
private void getqualityvideo() {
/*if (Prefernce.quality == null) {
preferencecamrecoder = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
} else {*/
if (Prefernce.quality.equals("QUALITY_LOW")) {
preferencecamrecoder = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
}
if (Prefernce.quality.equals("QUALITY_720P")) {
preferencecamrecoder = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);
}
if (Prefernce.quality.equals("QUALITY_HIGH")) {
preferencecamrecoder = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
}
// }
}
Problem:
when start application
private void startServes() {
btnStart = (ImageView) findViewById(R.id.StartService);
btnStart.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
v.startAnimation(mAnimationImage);
Intent intent = new Intent(MainActivity.this, RecorderService.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startService(intent);
changeCamera
.setEnabled(false);
btnStart.setEnabled(false);
setings.setEnabled(false);
moveTaskToBack(false);
}
});
}
in another class in method
getqualityvideo() error NullPointerException
error in this first line
if (Prefernce.quality.equals("QUALITY_LOW"))
why the quality variable is empty?
The reason is that you're setting Preference.quality in the onCreate method in your Preference class. So what's probably happening is that when you start your application in your other class, Preference.quality is going to be null because it was never initialized to anything. The reason is that the other class has no way to access the onCreate method in your Preference class as of now. onCreate is executed when an activity starts, but that doesn't seem to happen anywhere in your code. A solution could be to initialize public static String quality outside of your onCreate method but still within the Preference class,
public static String quality = "QUALITY_HIGH";
#Override
public void onCreate(Bundle savedInstanceState) {
//insert code here
}
The problem was merely a scope issue.
So I have a music application. I am trying to update the UI with the progress of the media player (like current time, current song, album cover) everytime the song changes. I found that using interfaces was a awesome magical way of communication between activity and fragments so I implemented an interface in my MusicManger class. My code will show what and how did it.
Two problems
1) Commented look below, ExecutorService seems to stop after one loop. No Errors in catch block (this is why I tagged with java)
2) Commented please look, All the System.out methods print but the UI doesn't update. I do believe I called the method from mainThread so it should update.
I'll show code in logical order will add titles in bold before code segment to tell you basic idea of code.
Passing UI references from fragment to MusicManager class, code below in Fragment class
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_item_songlist, container, false);
// Set the adapter
TextView musicManagerSongName, musicManagerCurrent, musicManagerTotal;
ProgressBar musicManagerProgress;
ImageView musicManagerImageView;
mListView = (AbsListView) view.findViewById(R.id.slist);
musicManagerSongName = (TextView)view.findViewById(R.id.textView12);
musicManagerCurrent = (TextView)view.findViewById(R.id.textView10);
musicManagerTotal = (TextView)view.findViewById(R.id.textView11);
musicManagerProgress = (ProgressBar)view.findViewById(R.id.progressBar);
musicManagerImageView = (ImageView)view.findViewById(R.id.imageView2);
MainActivity.mediaPlayer.passUIReferences(musicManagerSongName, musicManagerCurrent, musicManagerTotal, musicManagerProgress, musicManagerImageView, view);
// line above is a method within MusicManager that takes the references will show code next!
ImageButton playbutton = (ImageButton)view.findViewById(R.id.playbuttonbar);
ImageButton nextButton = (ImageButton)view.findViewById(R.id.nextbuttonbar);
ImageButton backButton = (ImageButton)view.findViewById(R.id.backbuttonbar);
ImageButton toggleButton = (ImageButton)view.findViewById(R.id.shufflebuttonbar);
ImageButton pausebutton = (ImageButton)view.findViewById(R.id.pausebuttonbar);
playbutton.setBackgroundResource(R.drawable.playbuttonbar);
playbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
MainActivity.mediaPlayer.stateChange(1);
}catch(Exception e) {
}
}
});
backButton.setBackgroundResource(R.drawable.backbutton1);
nextButton.setBackgroundResource(R.drawable.nextbutton1);
toggleButton.setBackgroundResource(R.drawable.shufflebuttonselected);
pausebutton.setBackgroundResource(R.drawable.pausebutton1);
pausebutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
MainActivity.mediaPlayer.stateChange(0);
} catch (Exception e){
}
}
});
mListView.setAdapter(mAdapter);
((MainActivity) mListener).restoreActionBar();
return view;
}
As Commended above the code that is located in MusicManager class that takes references and stores them. Also shows interface implementation with MusicManager class. And the Executor service
public void passUIReferences(View... views) {
this.uiElements = views;
}
private ExecutorService executorService = Executors.newSingleThreadExecutor();
private MediaplayerUpdateInterface uiUpdateInterface;
public MediaPlayerManager(MediaplayerUpdateInterface inter) {
this.player = new MediaPlayer();
this.uiUpdateInterface = inter;
// The below line starts the single thread while loop for excutorservice and only loops and prints "this" once after I start one song then it never loops again
executorService.submit(new Runnable() {
#Override
public void run() {
while(true) {
if (player.isPlaying() && uiElements != null) {
System.out.println("this");
uiUpdateInterface.updateUI(uiElements, 0);
}
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
public interface MediaplayerUpdateInterface {
public void updateUI(View[] views, int type);
}
Finally some code from MainActivity class that actually is suppose to update the UI note that both println's work as expected but only once as stated above because of the executorservice issue
public static MediaPlayerManager mediaPlayer = new MediaPlayerManager(new MediaPlayerManager.MediaplayerUpdateInterface() {
#Override
public void updateUI(View[] views, int type) {
System.out.println("check1 " + type);
updateMediaplayerViews(views, type);
}
});
private static void updateMediaplayerViews(View[] views, int type)
{
switch(type) {
case 0:
System.out.println("that?");
((TextView)views[0]).setText(mediaPlayer.getCurrentSongInfo().getName().length() > 22? mediaPlayer.getCurrentSongInfo().getName().substring(0, 19)+"..." : mediaPlayer.getCurrentSongInfo().getName());
break;
}
views[views.length - 1].invalidate();
}
The view array is shown perviously! Also the last view in the array is shown as the main view for songlist fragment.
I am sorry for all the code I've tried to debug it as you can see from my println's there is just something I am unaware of going on here.
Ok so there was an error that I needed to catch to see within the following code:
private static void updateMediaplayerViews(View[] views, int type)
{
switch(type) {
case 0:
System.out.println("that?");
((TextView)views[0]).setText(mediaPlayer.getCurrentSongInfo().getName().length() > 22? mediaPlayer.getCurrentSongInfo().getName().substring(0, 19)+"..." : mediaPlayer.getCurrentSongInfo().getName());
break;
}
views[views.length - 1].invalidate();
}
The issue is I was trying to change the view from a different thread then the one which created it. Solving it was pretty long and painful but basically I made it nonstactic used more interfaces then used the famous
Mainactivity.runOnUiThread(new Runnable(....));
I have been developing a simple touch handler for Android with the possibilites of firing callbacks like onUpdate (when the screen is touched) without having to setup threads. My problem is that my knowledge of Java is fairly limited and i can't do it because i know very little of how to use interfaces. I'm pretty sure that my problem may be a simple typo or something, but i get a NullPointerException when i execute the method from the touch handler (which processed the touch information) so that i can do what i need in the main activity class.
This is the main class code (cut from the irrelevant stuff):
//package and imports
public class Test extends Activity implements TouchHelper {
StringBuilder builder = new StringBuilder();
TextView textView;
TouchReader touchReader;
List<TouchTable> touchTablesArray;
TouchTable touchTable;
public static final String Tag = "TouchTest";
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
textView = new TextView(this);
Log.d(Tag, "TextView initialized " + textView);
textView.setText("Touch and drag (multiple fingers supported)!");
touchReader = new TouchReader(textView);
Log.d(Tag, "touchReader initialized");
touchTablesArray = touchReader.getTouchTables();
setContentView(textView);
}
#Override
public void onTouchUpdate(int pointerId)
{
Log.d(Tag, "onTouchUpdate called");
touchTable = touchTablesArray.get(pointerId);
Log.d(Tag, "touchTable get successful");
//writing on stringbuilder
}
}
This is the code of the handler itself:
//package and imports
public class TouchReader implements OnTouchListener
{
public final static String Tag = "TouchReader";
List<TouchTable> touchTables;
TouchHelper helper;
TouchTable touchTable = new TouchTable();
public TouchReader(View view)
{
view.setOnTouchListener(this);
touchTables = new ArrayList<TouchTable>(10);
Log.d(Tag, "TouchReader initialized");
}
public boolean onTouch(View v, MotionEvent event)
{
synchronized(this)
{
//all the common code handling the actual handling, with switches and such
touchTables.add(pointerId, touchTable); //obviously the pointerId is defined earlier
Log.d(Tag, "Values updated");
helper.onTouchUpdate(pointerId); //the exception is here
Log.d(Tag, "Update called");
}
return true;
}
public List<TouchTable> getTouchTables()
{
synchronized(this)
{
return touchTables;
}
}
}
As you can see the error is most likely due to my inability to correctly use an interface, and yet all the official docs confused me even more.
Finally, the tiny code of the interface:
//package
public interface TouchHelper
{
public void onTouchUpdate(int pointerId);
}
I hope this question isn't too noobish to post it here :)
EDIT: Thanks to all for the help, in the end i followed Bughi's solution.
Your TouchHelper helper; is null, it needs a instance of the interface to be able to call methods on it -in your case the main activity class that implements your interface-
Make a set method for the listener
public void setOnTouchListener(TouchHelper helper)
{
this.helper = helper;
}
Then call it from on create:
public class Test extends Activity implements TouchHelper {
...
#Override
public void onCreate(Bundle savedInstanceState)
{
...
touchReader = new TouchReader(textView);
touchReader.setOnTouchListener(this);
...
}
}
Also add a null check to your on touch method:
public boolean onTouch(View v, MotionEvent event)
{
synchronized(this)
{
//all the common code handling the actual handling, with switches and such
touchTables.add(pointerId, touchTable); //obviously the pointerId is defined earlier
Log.d(Tag, "Values updated");
if (helper != null)
helper.onTouchUpdate(pointerId); //the exception is here
Log.d(Tag, "Update called");
}
return true;
}
If the NullPointerException is here:
helper.onTouchUpdate(pointerId);
Then simply helper is null, where do you initialize it?
I see that you define it:
TouchHelper helper;
But do you ever have?
helper = ...
I know this is old, but I was stuck on this myself. Sam's post above helped me think of it.
I finally added an onAttach method that that checks that the interface is initialized as well as implemented to the main activity that it interfaces with. I added a Log.i inside the main activity to test.
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mainActivityCallback = (OnSomethingSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnSomethingSelectedListener");
}
}
In TouchReader you define a TouchHelper but nowhere in the code an object is created or an existing object is assigned to that attribute. So it is still null when you try to use it.
helper is null in your in TouchReader
To fix this make the TouchReader take a TouchHelper:
public TouchReader(View view, TouchHelper helper) {
...
this.helper = helper;
...
}
Then in your activity:
touchReader = new TouchReader(textView, this);
Try initializing it in your constructor; all reference that aren't initialized are set to null.
// I see no reason why this should be a member variable; make it local
StringBuilder builder = new StringBuilder();
TextView textView;
TouchReader touchReader;
List<TouchTable> touchTablesArray;
TouchTable touchTable;
public TouchReader(View view)
{
// textView is null
// touchReader is null
view.setOnTouchListener(this);
// why "10"? why a List of touchTables and a touchTable member variable? why both?
touchTables = new ArrayList<TouchTable>(10);
Log.d(Tag, "TouchReader initialized");
// touchTable is null;
}