How to edit dynacmically added buttons from class other then main activity - java

I am learning to program in android to transfer my java application to my phone but I am running into a very basic problem I can't find the answer for.
I am dynamically adding buttons and changing there color when I press them. This works fine when I do it in main activity but when I try to clean up my code by making seperate classes it breaks.
Code is provided below.
This is my first question here so I might have messed up the formating sorry in advance.
Main activity
public void test(View view) {
View v = getLayoutInflater().inflate(R.layout.newwindow, null);
ViewGroup insertPoint = (ViewGroup) findViewById(R.id.testlayout8);
insertPoint.addView(v, 0, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
public void multbut(View view) {
view.setBackgroundColor(11111);
}
New Java class extends Main activity
public void multbut2(View view) {
view.setBackgroundColor(11111);
}

you can not pass views to another classes other than MainActivity.
Java is using "Pass by value" for non-primitive objects but you should do your changes on the same reference of the view that you added to the container(screen).
when you pass the views, java will make a copy from them in the new class and that's cause of your problem

Related

How can I turn off localbroadcast listener in recyclerview adapter? Of is there a better way to communicate with an activity called from it?

I'm new to SO and fairly new to coding, so please accept my apologies in advance if I break rules or expectations here.
I have an unusual setup involving two recyclerViews, which I'll explain here and also paste a simplified version of the code below (as there is so much not relevant to this question).
In what I'll call verticalRecyclerViewActivity, a verticalRecyclerViewAdapter is called, with data it fetches from Firebase and loads into arrayLists.
If the user clicks on an item in the vertical recyclerview, a new dialog fragment which I'll call horizontalRecyclerViewDialogFragment is inflated, and that loads what I'll call horizontalRecyclerView (which has similar items to the vertical one, in more detail, with options to click on them to review them).
If the user clicks on an item in the horizontalRecyclerView, a new activity which I'll call reviewItem is started (through an Intent). When the user submits their review, it finishes and returns (through the backstack) to the horizontal RecyclerView. That can also happen if they press the back button without actually submitting a review. That all works fine, but I need the horizontalRecyclerView to show that they have (or haven't) reviewed the item and state the score they gave it in a review.
Calling notifyDataSetChanged won't work for this because of how information comes through two recyclerViews and Firebase calls (or, at least, it would be very inefficient).
I've tried using startActivityForResult (I know it's deprecated, but if I could get that to work I could try using the newer equivalent which I don't yet understand) but the problem is that the result is returned to the original (VerticalRecylcerView) activity, which is two recyclerView adapters and one fragment beneath what needs to be updated, and I don't know how to pass that data to the horizontal Recyclerview.
I've also tried using interfaces but was unable to pass it through the Intent (tried using Parcelable and Serializable, but it seems neither can work in this situation?).
Since the review is updated on Firebase, I could have the horizontal Recyclerview listen for a change, but that seems very inefficient?
So I've found a solution using localBroadcast (which I know is also deprecated). The Intent (with the review score) is transmitted when it is reviewed and received in the horizontal recyclerView adapter. But when and how should I unregister the adapter? Ideally the receiver would be turned on when the user goes to the Review activity and turned off once the user returns from that activity and the (horizontal) recyclerView holder is updated, whether the review is successfully submitted or whether the user just presses the back button and never submits a review.
My question is similar to this one: How to unregister and register BroadcastReceiver from another class?
That is noted as a duplicate of this one: How to unregister and register BroadcastReceiver from another class?
There's a lot in those questions I don't understand, but the important difference I think between their and my cases is that I would just like the receiver to know when a review is submitted, and ideally be unregistered then, or possibly when the viewHolder is recycled, which I tried but also didn't work since it's not connected to the viewHolder (should it be?).
Any help would be much appreciated, thank you!
public class verticalRecyclerViewActivity extends AppCompatActivity {
// Loads an XML file and assembles an array from Firebase.
mVerticalRecyclerView = findViewById(R.id.verticalRecyclerView);
verticalRecyclerViewAdaptor mVerticalRecyclerViewAdaptor = new verticalRecyclerViewAdaptor (this); // also pass other information it needs
mVerticalRecyclerView .setAdapter(mVerticalRecyclerViewAdaptor);
}
public class verticalRecyclerViewAdaptor extends RecyclerView.Adapter<verticalRecyclerViewAdaptor.singleHolder> {
// Usual recyclerView content
holder.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
horizontalRecyclerViewFragment mHorizontalRecyclerViewFragment = new horizontalRecyclerViewFragment();
// lots of arguments passed it needs.
FragmentManager fragmentManager = ((FragmentActivity) view.getContext()).getSupportFragmentManager();
mHorizontalRecyclerViewFragment.show(fragmentManager, null);
}
public class mHorizontalRecyclerViewFragment extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getActivity().getApplicationContext(); // Not sure why I need this, but it works.
View horizontalRecyclerViewView = getActivity().getLayoutInflater().inflate(R.layout.horizontal_recyclerview_holder, new CardView(getActivity()), false);
Dialog horizontalRecyclerViewDialog = new Dialog(getActivity());
horizontalRecyclerViewDialog.setContentView(horizontalRecyclerViewView);
mHorizontalRecyclerView = horizontalRecyclerViewView.getRootView().findViewById(R.id.horizontalRecyclerView);
mHorizontalRecyclerViewAdapter = new horizontalRecyclerViewAdapter (mContext)
// Other arguments passed
mHorizontalRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(),
LinearLayoutManager.HORIZONTAL, false));
mHorizontalRecyclerView.setAdapter(mHorizontalRecyclerViewAdapter);
}
public class horizontalRecyclerViewAdapter extends RecyclerView.Adapter<horizontalRecyclerViewAdapter.horizontalRecyclerViewHolder> {
public horizontalRecyclerViewAdapter(){}
// Blank constructor and also one with lots of arguments for it to work.
public horizontalRecyclerViewAdapter.horizontalRecyclerViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.horizontal_recyclerview_adaptor_holder, parent, false);
return new horizontalRecyclerViewAdapter.horizontalRecyclerViewHolder(view);
}
public void onBindViewHolder(#NonNull horizontalRecyclerViewHolder holder, int position) {
// Connect up various views.
holder.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
LocalBroadcastManager.getInstance(mContext).registerReceiver(reviewSubmittedListener, new IntentFilter("reviewSubmitted"));
Intent reviewNow = new Intent(view.getContext(), ReviewActivity.class);
// Put extra details with the intent
view.getContext().startActivity(reviewNow);
}
BroadcastReceiver reviewSubmittedListener = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent reviewFinishedIntent) {
int reviewScore = reviewFinishedIntent.getExtras().getInt("reviewScore");
// Update the horizontal RecyclerView with the information received from the review Activity.
}
};
}
public class ReviewActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_review_item);
// Set up the review, using Firebase and data passed through the intent.
}
public void submitReview() {
// Check that the review is complete/valid and submit it through Firebase
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(ReviewItemActivity.this);
Intent reviewFinishedIntent = new Intent("reviewSubmitted");
reviewFinishedIntent.putExtra("reviewScore", overallScore);
LocalBroadcastManager.getInstance(this).sendBroadcast(reviewFinishedIntent);
finish();
}
If you are using RxJava you can use the RxBus else you can use one of many EventBus implementation for this.
If that is not the path you want to take then you can have a shared view model object that can be used only for communication between fragments see this article.

android onclick () not responding

I am new to android programming and trying to develop TIC TAC TOE game. I have created gameLogic() method and the problem is this that it is not working as it is expected to do, means on click of ImageView none of the images is getting displayed. Any help will be highly appreciated.
This is my code:
public class MainActivity extends AppCompatActivity {
public void gameLogic(View view) {
ImageView tappedView = (ImageView)view;
tappedView.setTranslationY(-3000f);
tappedView.setImageResource(R.drawable.black);
tappedView.animate().translationYBy(3000f).setDuration(500);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
public void onClick (View view) {
ImageView tappedView = (ImageView)view;
tappedView.setTranslationY(-3000f);
tappedView.setImageResource(R.drawable.black);
tappedView.animate().translationYBy(3000f).setDuration(500)
Try changing the class name to onClick and go to XML the page where you designs the button and declare onClick method in the image view button.
All you have to do is let the design page know where the code is by providing a common name to look for which is on click.
Also you have to add an onClick listener which listens for the button to click as the code should know which button it is linked to.
So declare a setonclicklistener method on the button Id and you're set to go.
Also write the code within the override after the setting of the layout.
Hope you understood :)
If you're new try head first Android it'll be really helpful I know how struggling it is during the initial days.

Where should I put my ShowCaseView builder in this case?

I have this in my MainActivity.java:
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupHomeScreen();
}
public void setupHomeScreen() {
File latestPic = getMostRecentSnappiePicture();
if(latestPic != null){
//display pic
LinearLayout layout = (LinearLayout) findViewById(R.id.mainLayout);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
layout.setBackground(Drawable.createFromPath(latestPic.getAbsolutePath()));
}
else{
layout.setBackgroundDrawable(Drawable.createFromPath(latestPic.getAbsolutePath()));
}
//hide tutorial
findViewById(R.id.howitworks).setVisibility(View.INVISIBLE);
}
else{
//show tutorial
findViewById(R.id.howitworks).setVisibility(View.VISIBLE);
new ShowcaseView.Builder(this)
.setTarget(new ActionViewTarget(this, ActionViewTarget.Type.HOME))
.setContentTitle("ShowcaseView")
.setContentText("This is highlighting the Home button")
.hideOnTouchOutside()
.build();
}
}
}
As you can see, in onCreate, it calls setupHomeScreen and checks if a file exists. If it doesn't exist, it displays a tutorial "howitworks" layout image as well as building a showcase view.
So this all works fine and well. The only issue comes when trying to leave the activity while the showcaseView is still there, OR sometimes even after you exit the showcase view and try and launch the new activity, this error comes up: ShowcaseView - width and height must be > 0
As you can see in the answers, the solution is to only create the showcase view in the callback after the original view has been created like so:
someView.post(new Runnable() {
#Override
public void run() {
// my ShowcaseView builder here
}
});
The only thing is, I have no idea where to put this, since my showcase view should only show up if the file from getMostRecentSnappiePicture() is null. So how can I put the view creation callback inside of my logic to check that that file is null first?
it looks like you're highlighting the HOME button instead of the 'howitworks' view. Try switching the line
.setTarget(new ActionViewTarget(this, ActionViewTarget.Type.HOME))
with
.setTarget(new ViewTarget(R.id.howitworks,this));
Also, the following video might help. It's 20 minute tutorial on how to use ShowCaseView inside an activity with 3 buttons. He is declaring an onClickListener where he changes programmatically the TargetView highlighted by the showCaseView
https://www.youtube.com/watch?v=3zdeFSBplps
The video is in spanish, but at least you'll be able to follow the steps, since he's writing the code from scratch.

How to update TextView in parant Activity based on current ImageView in ViewPager

Im new to android dev and Im currently struggling with this annoying problem for days.
Tried google and many articles. None helped. Also tried IRC for some on line help but couldnt get a straight answer. Maybe you can direct me in the right direction...
My app is basically a ViewPager that holds a single Fragment class. Inside the Fragment class, there is a switch case block that determines based on position, which image to load from the resources folder. The thing is, I have a TextView View in my parent Activity that needs to get updated according to the current picture. Like an image title.
I used:
TextView tv = (TextView) getActivity().findViewById(R.id.titleTV);
in onCreateView() to get access to that TextView from within the Fragment. And added a:
tv.setText("Picture 1");
for instance, to the switch case block. This way, when a picture is shown, the text view gets updated.
The problem is, the method that retrieves a new Fragment with each slide, getItem(int position) in the parent Activity, gets called twice to load more than one Fragment to memory. This causes the current position to be one int ahead. Meaning, the switch case stands on case 0 for instance and it shows a specific picture, but the text gets updated from case 1.
I cant get it to work properly because of that.
What am I doing wrong??
Thank you
If you are able to determine text based on current fragment position only you can use ViewPager.OnPageChangeListener. In you Activity implement that interface and use it by calling mViewPager.setOnPageChangeListener(SomeOnPageChangeListener). Determine what text put into TextView with OnPageChangeListener.onPageSelected() method.
Unfortunately there is a bug in SDK and onPageSelected() is not called for page at position 0 when it is shown for the first time. You can find workaround under above link, use ViewPager.setCurrentPage(1) or just set text for that position manually in onCreate() method.
Example code:
//activity onCreate()
protected void onCreate(Bundle savedInstanceState) {
...
mTextView = (TextView) findViewById(R.id.text_view);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
String someText = determineText(position)
mTextView.setText(someText);
});
//because onPageSelected will not be called for page 0 first time
String someText = determineText(0)
mTextView.setText(someText);
}
private String determineText(int position){ //static?
//switch?
}
Ok I'll give a piece of code, you can use that, not just for a String but for any data at all that you want to send to the Activity from the Fragment, or even from other type of classes between each other.
That's the type of code is also used on .'setOnSomethingHappenListener(listener);'
public interface TitleChangeListener{
public void onTitleChanged(String title);
}
then on your activity you will
public class MyActivity extends Activity implements TitleChangeListener{
#Override
onTitleChanged(String title){
// set here your value
}
}
then on your fragment
public void MyFragment extends Fragment{
private TitleChangeListener listener;
#Override
public void onAttach(Activity activity){
if(activity instanceof TitleChangeListener)
listener = (TitleChangeListener)activity;
else
// throw some error ???
}
#Override
public void onResume(){
listener.onTitleChanged("my title");
}
}
and remember I typed all of that by heart, there is bound to have some small mistakes.
But just follow the idea and you'll be fine.

Refreshing a view inside a fragment

I have searched the numerous questions that look like this one, but haven't found my answer in any of them.
I have an activity that has 3 tabs accessible through the action bar. I achieved this by adding 3 fragments that inflate a custom view I made extending the view class.
At the moment the database changes, I try to refresh the view in my tab by calling invalidate()/postinvalidate(), but this does not work. The same is true for calling onCreateView of the fragment just as many other options I considered.
When I go to another tab and go back, however, the change has been made and my view is updated as it should be.
How can I simulate the same thing that happens when changing to another tab? What does happen. I tried to look at the Fragment lifecycle (tried to call onCreateView()) to figure it out but it just doesn't want to refresh/redraw as it should.
The data is loaded properly, as the data is changed when I change to another tab.
I deleted some of the code as it is no longer relevant. I implemented Cursorloaders instead of my own Observer pattern to notify a change. This is my main activity right now.
The question is what should I do now if I want to redraw the view inside these fragments. If I apply fragmentObject.getView().invalidate() it does not work. I'm having the same problem as before, but now my Observer to notify a change in the database is properly implemented with loaders.
public class ArchitectureActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ActionBar actionbar = getActionBar();
actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab EditTab = actionbar.newTab().setText("Edit");
ActionBar.Tab VisualizeTab = actionbar.newTab().setText("Visualize");
ActionBar.Tab AnalyseTab = actionbar.newTab().setText("Analyse");
Fragment editFragment = new EditFragment();
Fragment visualizeFragment = new VisualizeFragment();
Fragment analyseFragment = new AnalyseFragment();
EditTab.setTabListener(new MyTabsListener(editFragment));
VisualizeTab.setTabListener(new MyTabsListener(visualizeFragment));
AnalyseTab.setTabListener(new MyTabsListener(analyseFragment));
actionbar.addTab(EditTab);
actionbar.addTab(VisualizeTab);
actionbar.addTab(AnalyseTab);
ArchitectureApplication architectureApplication = (ArchitectureApplication)getApplicationContext();
architectureApplication.initialize();
getLoaderManager().initLoader(0, null, this);
getLoaderManager().initLoader(1, null, this);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (id == 0){
return new CursorLoader(this, GraphProvider.NODE_URI , null, null, null, null);
} else if (id == 1){
return new CursorLoader(this, GraphProvider.ARC_URI , null, null, null, null);
}
return null;
}
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// Reloading of data, actually happens because when switching to another tab the new data shows up fine
Log.e("Data", "loaded");
}
public void onLoaderReset(Loader<Cursor> loader) {
}
}
Don't try to call onCreateView() yourself... it's a lifecycle method and should be called only by the framework.
Fragments are re-usable UI components. They have their own lifecycle, display their own view, and define their own behavior. You usually don't need to have your Activity mess around with the internal workings of a Fragment, as the Fragment's behavior should be self-contained and independent of any particular Activity.
That said, I think the best solution is to have each of your Fragments implement the LoaderManager.LoaderCallbacks<D> interface. Each Fragment will initialize a Loader (i.e. a CursorLoader if you are using a ContentProvider backed by an SQLite database), and that Loader will be in charge of (1) loading the data on a background thread, and (2) listening for content changes that are made to the data source, and delivering new data to onLoadFinished() whenever a content change occurs.
This solution is better than your current solution because it is entirely event-driven. You only need to refresh the view when data is delivered to onLoadFinished() (as opposed to having to manually check to see if the data source has been changed each time you click on a new tab).
If you are lazy and just want a quick solution, you might be able to get away with refreshing the view in your Fragment's onResume() method too.
I had a similar (although not identical) problem that I could solve in the following way:
In the fragment I added
public void invalidate() {
myView.post(new Runnable() {
#Override
public void run() {
myView.invalidate();
}
});
}
and I called this method from the activity when I wanted to refresh the view myView of the fragment. The use of post() ensures that the view is only invalidated when it is ready to be drawn.
I've found a workaround to refresh the view inside my fragment. I recreated (new CustomView) the view every time the database has been updated. After that I call setContentView(CustomView view). It looks more like a hack, but it works and nothing else that I tried does.
Although my problem was not actually solved, I gave the reward to Alex Lockwood. His advice on Loaders made my application better and it caused me to keep looking for a solution that I eventually found.
I had the same issue.
My solution was detach fragment and attach it again.
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment f = getFragment(action);
if(forceUpdate)
{
fragmentTransaction.detach(f);
fragmentTransaction.attach(f);
}
fragmentTransaction.replace(R.id.mainFragment, f);
fragmentTransaction.commit();
currentAction = action;
The fastest solution working for me:
#Override
public void onPause() {
super.onPause();
if (isRemoving() && fragmentView != null) {
((ViewGroup) fragmentView).removeAllViews();
}
}

Categories

Resources