I am using Android data binding in a MVVM framework. I have a ViewPager setup with a corresponding PagerAdapter. Some of the pages may contain videos. The issue that I am having is that the instantiateItem method in the PagerAdapter always executes the next view in the PagerAdapter instead of the current view. For example say page 1 has no video, but page 2 does. When the user views page 1, the video in page 2 starts playing. Here is the instantiateItem method:
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater layoutInflater = (LayoutInflater) getApplicationContext().getSystemService(Service.LAYOUT_INFLATER_SERVICE);
ViewDataBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.main_layout, null, false);
Post post = posts.get(position);
myViewModel = new PostModel(post);
binding.setVariable(BR.model, myViewModel);
container.addView(binding.getRoot());
return binding.getRoot();
}
How can I make the instantiateItem execute the current view instead of the next view? Is the issue related to setOffscreenPageLimit?
You can't because it's main idea of ViewPager - it preload next N pages in order to make swiping smooth. Also actually you can setup it in order to see part of next view.
I think you should use OnPageChangeListener, and manager start/stop of playing of your video when it needed here.
Btw, some advices - you can use that library in order to not write that boilerplate code at all. Here my example of usage.
Related
In my activity I try to call a method from a class but it's not getting the right values.
This is ActivityTwo:
int id = intent.getIntExtra("id", 0);
LayoutInflater inflater = getLayoutInflater();
View mainActivity = inflater.inflate(R.layout.activity_main, null);
LinearLayout eventsLayout = mainActivity.findViewById(R.id.eventsLayout);
Log.d("ACTIVITY_eventlayout", String.valueOf(eventsLayout)); // gives the layout perfectly
Event.RemoveSpecific(eventsLayout, id);
finish();
This is the class(Event) with the method:
public static void RemoveSpecific(LinearLayout layout, int id){
View event = layout.findViewById(id);
Log.d("INSIDE_removespecific", String.valueOf(event));// event is null
layout.removeView(event);
}
And in my MainActivity it's working fine:
LinearLayout eventsLayout = findViewById(R.id.eventsLayout);
View event = eventsLayout.findViewById(id);
//Log.d("MAIN_event", String.valueOf(event1)); // gives it perfectly
eventsLayout.removeView(event);
I also add this view in my MainActivity and use .setId(id) to give it the right id. So my question is, why is the View in my class method null, while I pass the right LinearLayout from my activityTwo?
Where does the id come from?
I have the Event class which contains an id, name, date, & description. This class also has a static ArrayList called eventsList. Whenever the user creates a new reminder I create a new Event using my class and giving it an id as Event.eventsList().size() (after adding it to my eventsList [so the first event id is always 1]), then I create a new View and pass the recently created Event details and use setId() to give it an id [1]. Then in this View I have a button which has an onClickListener which passes the given View id and the LinearLayout (where the View was added) to my Event.removeSpecific() method.
App flow:
The user clicks a button in MainActivity to create an event, then the button click then use startActivityForResult() with an intent to open a new Activity where the user puts in a name,description and date for the event, then I create an intent with putExtra() methods and then use setResult(RESULT_OK, resultintent) to send the data back to MainActivity. Now when the MainActivity gets the data back I use .getStringExtra() to create a new Event object with the data and add it to may Event.eventsList ArrayList, then I use the following:
LayoutInflater inflater = getLayoutInflater();
View event_Exmaple = inflater.inflate(R.layout.event_example, null);
and set the textView's with the data I got, and use event_example.setId(Event.eventsList.size()) to give it an id.
After that I add this to my LinearLayout (which I declared already in the MainActivity):
eventsLayout.addView(event_example)
Now I mentioned that the Event class has a date field. I use that to set up an alarm with AlarmManager. With the AlarmManager I send the current Event object's data through the intent and when the user gets the notification it opens up a new Activity which gets the given Event object data (with intent.putExtra()'s) and that's the part where the user clicks on a Button. I want to remove the given Event object's View from my LinearLayout in the MainActivity XML.
well since you didn't share where the id value come from (from where the intent gets its data/how you put the values in it/where the values come from), I assumed this solution :
whenever I use a 'switch' statement with View Ids it tells me a very important warning which is
Resource IDs will be non-final in Android Gradle Plugin version 5.0, avoid using them in switch case statements
that means that view Ids doesn't have a stable id in some cases that you shouldn't consider them to be final.
I guess in your case when you get an id to your event view and then inflate the layout view, the inflate procedure involve changing the 'int' id value of the event view so whenever you query the old id it returns null since the event view inside the inflated layout view now has a new 'int' id value.
I don't know why it works in main activity since you didn't share full code where you get the id from.
so I guess the only solution is that you should find a way to get a fresh copy of the id after you inflate the view you want to work on in the method.
Update
the problem is with this block of code
View mainActivity = inflater.inflate(R.layout.activity_main, null);
everytime you inflate a new layout view thinking that you have the one used by main activity back there but instead you're creating an entirely new one with new views and new Id's.
that would be why you could get the view in main activity but you can't get it from a newly inflated view.
Update
if you need to remove this item using the id and have a static array stored in the view custom class, call a getter on the static array and remove the item from it instead of trying to remove the view itself.
a better implementation of the whole situation is to store these events in a database using room instead of the static array, that way you could delete/add/edit any event anytime with just one line of code, if you're willing to do this you can start by reading the developers documentation of room.
I'm making a to-do list app and after user presses the button I create a new GridLayout(and all the data about time and name of the task inside of it) and add it into my RelativeLayout. How do I save those GridLayouts in UI so after the activity is destroyed and launched again those layouts are there.
After pressing the button I trigger the Create Activity method
public void CreateActivity(String name,int hours, int minutes,int i)
{
RelativeLayout.LayoutParams relparams= new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,RelativeLayout.LayoutParams.WRAP_CONTENT);
relparams.addRule(RelativeLayout.BELOW,i);
relparams.setMargins(0,50,0,100);
Glayouts.add(new GridLayout(this));
Glayouts.get(i+1).setBackgroundColor(Color.GRAY);
Glayouts.get(i+1).setMinimumWidth(relative.getWidth());
Glayouts.get(i+1).setId(i+1);
Glayouts.get(i+1).setPadding(10,0,0,0);
GridLayout.LayoutParams namee = new GridLayout.LayoutParams();
namee.columnSpec = GridLayout.spec(0);
namee.rowSpec = GridLayout.spec(0);
namee.setGravity(Gravity.LEFT);
final TextView Actname = new TextView(this);
Actname.setText(name);
GridLayout.LayoutParams checkbox = new GridLayout.LayoutParams();
checkbox.columnSpec = GridLayout.spec(1);
checkbox.rowSpec = GridLayout.spec(0);
checkbox.setGravity(Gravity.RIGHT);
CheckBox check = new CheckBox(this);
// ADDING TO LAYOUT
Glayouts.get(i+1).addView(Actname,namee);
Glayouts.get(i+1).addView(check,checkbox);
relative.addView(Glayouts.get(i+1),relparams);
Theoretically when you extends View, then you can also override onSaveInstanceState and onRestoreInstanceState methods, where you must provide your own SavedState class that typically extends BaseSavedState. You can find info on that here
In your case, your layout is dynamic, therefore this doesn't really work. To tell you the truth, your layout probably shouldn't be constructed this way, you should be rendering the grid using a RecyclerView based on a "model" that describes this layout, render the items of the grid via the RecyclerView.Adapter, and you should persist either the "model", or the data you use to construct this model along with the user-inputted state so that you can re-construct the model that will be rendered via your RecyclerView.
You can read more about RecyclerView here.
You can read more about data persistence here.
You can read about using onSaveInstanceState to save data in Activities/Fragments across config change and process death (but not finishing then restarting the app) here.
You can’t. The best way to save state is to use some persistence mechanism, for example database (I’d recommend Room as it is officially supported by Google).
After clicking a button, you should put all the needed information (name, hours, minutes) in the database and when Activity is created, you can read all persisted data and - basing on it - create all needed layouts again.
Another option is storing data in SharedPreferences - it is much easier to setup, so you can also start with this solution. Please note, I'm suggesting it as a first step in the world of persistency in Android, not as a preferred solution for storing data.
This problem has been beaten a million times, but unfortunately, no answer on SO has helped solve my issue.
Fundamentally, my question is that, in a RecyclerView with a CardView in a Fragment, when and where should the following tasks be performed:
Fetching data from a server to the local database on the user's device
Processing the so-fetched local data
Instantiating and setting the RecyclerView's adapter
Calling the RecyclerView's notifyfDataSetChanged()
The RecyclerView in my implementation presently calls back only getItemCount(). onCreateViewHolder() and onBindViewHolder() are never getting called back.
(I have seen this answer and a gazillion more.)
Here is the sequence of how its being done:
An AppCompatActivity instantiates a Fragment
The Fragment's layout contains a RelativeLayout with a toolbar and an include tag for a ViewPager. The ViewPager layout contains another include tag that sets a layout with a RecyclerView in a LinearLayout. The layout XML with the CardView is set in the RecyclerView's adapter:
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
Log.wtf(LOG_TAG, "Inside onCreateViewHolder()");
View myView;
LayoutInflater inflater;
inflater = LayoutInflater.from(parent.getContext());
myItemView = inflater.inflate(R.layout.my_row_layout, parent, false);
MyViewHolder myViewHolderItem = new MyViewHolder(myItemView);
return myViewHolderItem;
}
There's no ScrollView in any layout
The Fragment's onCreate() calls a method to fetch data from a server
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Some cache processing
args = getArguments();
myDBHelper = MyDBHelper.getInstance(getActivity());
Intent intent = getActivity().getIntent();
// Search comes from search intent
if (intent.getBooleanExtra(MyContract.MY_SEARCH_REQUEST, false))
{
...
getDataFromCloud("", true);
getDataFromLocalDB(false, item);
} // of if search intent
// Plain intent
else
{
getDataFromCloud("item", false);
getDataFromLocalDB(true, item);
}
} // of onCreate()
This data gets populated into a local database on the user's device
Another method in the Fragment's onCreate()filters the local data into an ArrayList
This ArrayList is presented in the RecyclerView - CardView combo finally to the user.
notifyDataSetChanged() is called in the Fragment's onActivityCreated(). But the fact is, its not making a difference anywhere it is called from. Also, the adapter is instantiated in the onPostExecute() of the AsyncTask that creates the ArrayList for the CardView from the local database.
Here's the log that results on running the app:
01-03 08:47:34.546 13489-14033/com.my A/MyDBHelper class: Create View-1 query: CREATE VIEW
01-03 08:47:34.562 13489-13489/com.my A/MyFragment: getItemCount: 0
01-03 08:47:34.584 13489-14033/com.my A/MyDBHelper class: Create View-2 query: CREATE TABLE
01-03 08:47:34.638 13489-13489/com.my A/MyFragment: getItemCount: 0
01-03 08:47:34.722 13489-13489/com.my A/MyFragment: getItemCount: 2
01-03 08:47:34.743 13489-13489/com.my A/MyFragment: getItemCount: 2
In the above log, the View-1 query fetches server data and the View-2 query processes the so-fetched local data.
I have log statements in the onCreateViewHolder() and onBindViewHolder(). As evident above, these methods are just not being invoked, despite a positive return from getItemCount(), which is the size of the ArrayList. The app just shows the toolbar, and an empty white card, despite there being two records or items to be shown.
Been staring at these logs for the past couple of days with no progress or clues. Many thanks in advance for your expert advice or pointers (and for saving what's left of my sanity)!
Finally, it was Jared Burrows's advice that drove the last nail in the coffin. BoyOboy, the only way to get it right is fight! (...almost always!)
Though I cannot pin-point what was going wrong, here's what I re-did:
Created a fresh new project with only the RecyclerView - CardView combo in a Fragment. Well worth the time and effort, in terms of the clarity it brought in.
Read-up this very good resource.
Switched back to the ListView implementation of my app, first got the CardView right, into the ListView
With a fresh new understanding of RecyclerView, systematically replaced all the ListView code with the RecyclerView counterpart
In the app, the custom RecyclerAdapter and ViewHolder live in their own, independent classes now
Was stuck for another couple of hours with the "RecyclerView: No Adapter attached; skipping layout” error; nothing was showing up in the Fragment. That's when Jared Burrows's advice pitched in.
The RecyclerView is now looking all hunky dory, waiting to throw up the next challenge: animation!
I'm attempting to implement accessibility on a few custom views for an Android app.
I've condensed what is done in the Google Authenticator app with no luck:
public class CardView extends RelativeLayout {
// ...
#Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
event.setClassName(this.getClass().getName());
event.setPackageName(this.getContext().getPackageName());
event.getText().add("Card Test");
return true;
}
}
All TalkBack reports back is "Double-tap to select" when it's inside a ListView or ViewPager.
Does ViewPager override accessibility events?
What do I need to do in order to have TalkBack say "Card Test" inside ViewPagers and ListViews like I expect it to?
For current versions of Android, you need to set the content description of the view.
myView.setContentDescription("Card Test");
ListView and associated classes expect you to use the onItemSelectedListener instead of assigning an onClickListener to each View (and rightfully so).
If incorporating alanv's suggestion, try to convince android system to read out the content description
by either
If(accessibilityModeIsEnabled())//custom method that checks context.getSystemService(Context.ACCESSIBILITY_SERVICE).isEnabled()
myView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
or AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED.
or requestFocus
Above should done when myView is visible. May be during onMesasure when width and height are both positive
If list view is still unable to do so, then try doing the above tricks on the first element of list view. Accessibility in Android varies devices to device and not one strategy fits all
I'm creating an application similar to the link given here, where it shows three images transition from one state to another when click.
1) Stage 1: When a series of listview of video files are stored in a video directory. How do i create this particular view into the ListView?
2) Stage 2: When a video file is being click it does not immediately play the video instead it shows a dialog box showing the detail of the file.
3) Stage 3: The user could either Exit, Select Play video or show roadmap details...
Could someone help me i'm kinna new in android/java here, i'm totally lost on how to start creating the above views like how do i populate the Listview with existing video files found in my video directory?
You realize that your basically asking someone to make the application for you? In any case, I shall try to give you some help to get started.
Stage 1: Do you know how to create a ListView? Here's an example:
listView = (ListView) findViewById(R.id.list_view);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, elements); //elements is a List<String>
listView.setAdapter(adapter);
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//What to do when an item is clicked
}
});
You can customize the look of the list items by creating a XML-file containing a single TextView and then use that when creating the ArrayAdapter (like R.layout.list_item).
If you want a completely custom View, like in the app in the link, you can create your own adapter and then implementing the View getView(int position, View convertView, ViewGroup parent) function, where you return the View you want to be displayed.
Example:
View row = convertView;
if (row == null) {
LayoutInflater mInflater = LayoutInflater.from(getContext());
row = mInflater.inflate(R.layout.bookings_list_item, parent, false);
}
return row;
You can create the elements list by counting the files in the video directory. I don't really know how to do this, but it shouldn't be too hard. Perhaps someone else can provide you with an answer if you don't find out yourself.
Stage 2: Implement this in the OnItemClickListener in the example above by showing a dialog.
Stage 3: Implement what the Dialog will do when it's buttons are pressed. Exit: dismiss popup. Play video: Launch an intent to a video player. I'm not sure about how to show roadmap details, but you can always use Google Maps in your app (there's a sample here).
Now, I hope you can get something useful from this. I hope I have provided you with enough details to be able to get started with researching and a little coding. :)