I am working on an android TV app and I am using the leanback library.
I want to customize the app layout "BrowseFragment". I want to remove the header view and only display the card list "rows".
Is it possible to do that or is there any other solution to achieve that?
I want to remove that
The above call actually needs to be in the OnCreate method instead of OnActivityCreated.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHeadersState(HEADERS_DISABLED);
}
You have to set the HeaderState like this:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHeadersState(HEADERS_DISABLED); // Add this line
}
There are two options:
/** The headers fragment is enabled and hidden by default. */
HEADERS_HIDDEN
/** The headers fragment is disabled and will never be shown. */
HEADERS_DISABLED
OnCreate, you have to set the Header:
setHeadersState(HEADERS_DISABLED); //To Diable the Header
setHeadersState(HEADERS_HIDDEN); //To Hide the Header
When you change the setHeaderState(HEADERS_DISABLED), the rows would be disabled and hidden too.
One way to do it is setHeaderPresenterSelector()
private void setupUIElements() {
setHeadersState(HEADERS_DISABLED);
setHeaderPresenterSelector(new PresenterSelector() {
#Override
public Presenter getPresenter(Object item) {
return new CustomPresenter();
}
});
}
you just need to override the getPresenter() method, and return new customized presenter that you need to implement.
Related
I've got a GuidedStepSupportFragment fragment like this.
public class SampleStepFragment extends GuidedStepSupportFragment {
#NonNull
#Override
public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
String title = "Title";
String breadcrumb = "Breadcrumb";
String description = "Description";
Drawable icon = getActivity().getDrawable(R.drawable.ic_videocam_black_24dp);
return new GuidanceStylist.Guidance(title, description, breadcrumb, icon);
}
#Override
public void onCreateActions(#NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
addAction(actions, ACTION_CONTINUE, "Action1");
addAction(actions, ACTION_BACK, "Action2");
}
}
Problem: When I scroll the action list, it shows like this;
But I want to something like this;
How can I disable this effect on my action list?
Thanks
I managed it and it wasn't easy to figure out.
There's no supported way of doing it, since the APIs that actually make this possible are package private or hidden from public use on purpose. (You can do it yourself, but you just end up copying classes from the leanback libraries.)
The solution:
#Override
public void onCreateActions(#NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
addAction(actions, GuidedAction.ACTION_ID_CONTINUE, "Action1");
addAction(actions, GuidedAction.ACTION_ID_CANCEL, "Action2");
// Run code delayed on mainThread (any other/better method can/should be used)
// It's delayed because if focus scroll is disabled, the list will stick to the top of the layout
new Handler(Looper.getMainLooper()).postDelayed(this::disableFocusScroll, 500);
}
private void disableFocusScroll() {
RecyclerView.LayoutManager layoutManager = SampleStepFragment.this.getGuidedActionsStylist().getActionsGridView().getLayoutManager();
try {
Method method = layoutManager.getClass().getMethod("setFocusScrollStrategy", int.class);
method.invoke(layoutManager, 1 /* FOCUS_SCROLL_ITEM */);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Log.e(TAG, "disableFocusScroll: ", e);
}
}
Full example
The explanation:
A GuidedStepSupportFragment requests a GuidedActionsStylist which is responsible for rendering the list items on the right side. source
The GuidedActionsStylist stylist inflates the layout lb_guidedactions.xml which contains a VerticalGridView source
The VerticalGridView extends BaseGridView and creates a GridLayoutManager as its layout manager. This GridLayoutManager is sadly package private and final... (android why..?). It has the method setFocusScrollStrategy which is used to determine how scrolling behaves.source
See the different focus scroll strategies:
/**
* Always keep focused item at a aligned position. Developer can use
* WINDOW_ALIGN_XXX and ITEM_ALIGN_XXX to define how focused item is aligned.
* In this mode, the last focused position will be remembered and restored when focus
* is back to the view.
* #hide
*/
#RestrictTo(LIBRARY_GROUP)
public final static int FOCUS_SCROLL_ALIGNED = 0;
/**
* Scroll to make the focused item inside client area.
* #hide
*/
#RestrictTo(LIBRARY_GROUP)
public final static int FOCUS_SCROLL_ITEM = 1;
/**
* Scroll a page of items when focusing to item outside the client area.
* The page size matches the client area size of RecyclerView.
* #hide
*/
#RestrictTo(LIBRARY_GROUP)
public final static int FOCUS_SCROLL_PAGE = 2;
So since the API is hidden, we just use reflection to expose the setFocusScrollStrategy method and set it to FOCUS_SCROLL_ITEM.
We can't do this immediately though, since without the default scroll setting, the list items will pop to the top of the layout and won't stay centered. So I added a delay of 500ms which is horrible... If you manage to find out when it's best to trigger this, let me know.
It turns out there is a much more elegant and simpler solution. It is enough to add to the body of onViewCreated in the class that inherits guideStepSupportFragment windowAlignment option (there are other alignment options):
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// ...
val gridView = guidedActionsStylist.actionsGridView
gridView.windowAlignment = VerticalGridView.WINDOW_ALIGN_BOTH_EDGE
// ...
super.onViewCreated(view, savedInstanceState)
}
Here is a simple way to disable VerticalGridView scrolling
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
VerticalGridView gridView = getGuidedActionsStylist().getActionsGridView();
gridView.setScrollEnabled(false);
super.onViewCreated(view, savedInstanceState);
}
I'm developing a simple application in which youb have different spots placed on google map.
When I click on a spot I get its details which are displayed in a GridViewPager.
For now my application is based on the GridViewPager sample available with the sdk.
Here is my layout for the spot details (nothing fancy)
<android.support.wearable.view.GridViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"/>
My problem now is that I'm not able to detect a Click event on a card.
I've tried this but it doesn't work.
public class DetailsActivity extends Activity implements GridViewPager.OnClickListener {
#Override
public void onClick(View v) {
}
I've also tried View.OnClickListener.
Have any idea ?
There are two issues here. First, if you really want to make the GridViewPager clickable, you need to tell it to listen for click events - just implementing the OnClickListener interface isn't sufficient. So you need to do something like this:
public class DetailsActivity ... {
#Override
public void onCreate(Bundle savedInstanceState) {
...
GridViewPager pager = (GridViewPager)findViewById(R.id.pager);
pager.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// put your onClick logic here
}
});
...
}
}
That being said, however, based on your description it sounds like what you actually want is to set up click handlers on individual pages within the grid, not on the entire grid. If so, then you'll need to do something similar but in each page's Fragment class. For example:
public class MyPageFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View result = inflater.inflate(...);
result.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// put your onClick logic here
}
});
...
return result;
}
}
Note: if you are using CardFragments in your GridViewPager, then you would probably set the OnClickListener in your onCreateContentView override. Otherwise, the above Fragment-based example should apply.
DogActivity is using a custom View. The custom view handles some logic and so has fields. When a particular field reaches a certain value, I want to start a fragment whose parent is DogActivity. How would I do that?
Is it advisable to put a callback inside a custom view so that it calls its parent activity? Or is there a simpler way?
When programming you should always look for consistency, i.e. look around you and see how similar stuff to what you want to do is done. The Android SDK makes heavy use of callback listeners, so they are the way to go here.
In fact we don't even need to know what kind of View your CustomView really is, we can build a general purpose solution. Don't forget to adapt/optimize according to your specific surroundings however. And think about abstraction and generalisation once you get to a point where all your Views are spammed with listeners!
You will need 3 things:
A listener interface
public interface OnCountReachedListener {
public void onCountReached();
}
A place to accept the listener and a place to alert the listener in your CustomView
public class CustomView extends View {
private int theCount;
private OnCountReachedListener mListener;
public void setOnCountReachedListener(OnCountReachedListener listener) {
mListener = listener;
}
private void doSomething() {
while (theCount < 100) {
theCount++;
}
// The count is where we want it, alert the listener!
if (mListener != null) {
mListener.onCountReached();
}
}
An implementation of the interface in your Activity
public class DogActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
View myView = new CustomView();
myView.setOnCountReachedListener(new OnCountReachedListener() {
#Override
public void onCountReached() {
Log.w("tag", "COUNT REACHED!");
// START YOUR FRAGMENT TRANSACTION HERE
}
});
}
}
For further information look at the source code of the View class and all the On**XY**Listener interfaces in the Android SDK. They should give you plenty to think about
What is the type of the field? Is it an EditText? SeekBar? Depending on the View, you'll be able to specify different listeners/callbacks to determine when they have changed and if they've reached a certain threshold. I would attach these listeners within onCreate of DogActivity. When the threshold is reached, use a FragmentTransaction to add your Fragment as the child of a container View in DogActivity.
This is on create of my activity and after setting content view , I am calling LoadButton().
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Loadbuttons();
}
public void LoadButtonss()
{
Here I am fetching data from database ,and plotting 50 buttons dynamically
}
So My Problem is : It is taking time in loading activity.
Any Idea how to LoadButtons() after Loading full activity. It should not wait for the ButtonLoad() function.
Any Idea how to do that?
paste this code in your activity class block:
#Override
protected void onStart() {
// TODO Auto-generated method stub
LoadButtons();
super.onStart();
}
According to the activity life cycle, you must call LoadButtons in onStart overrided method. Because activity will be visible in this state.
You could set the buttons to invisible and if you fetch the data you can update the UI through an AsyncThread. It is not the nicest way, but it works. Otherwise use instead of Buttons a CustomListView.
I would try this: Load buttons in onCreate and set View invisible. in onResume make it visible again.
View v;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
v= findViewById(R.layout.main);
v.setVisibility(View.INVISIBLE);
Loadbuttons();
}
#Override
protected void onResume() {
v.setVisibility(View.VISIBLE);
super.onResume();
}
public void LoadButtons()
{
FrameLayout layout = (FrameLayout) findViewById(R.id.MarkerLinearlayout);
//Here I am fetching data from database ,and plotting 50 buttons dynamically
}
I know this is a very basic question, however as a newbie i cant get to work around it.
So, I want to have multiple activities to use same the xml layout(consist for example of 1 imagebutton, and multiple textviews with different IDs). Now, for every activity, I want them to view the same layout but override the views with data unique to every activity. What is the best way to do this? And also, the imagebutton should open different URLs in a video player(youtube links).
And can somebody tell me what is the most practical way to learn android programming?
UPDATE
This is my current code:
public class TemakiActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contentviewer);
}
}
For example I have a textview with ID "descriptionviewer", and a button with ID "videolink", now, how do you code those in?
You can share the same layout file and the set the attributes for views in the onCreate(..) method of each activity.
If you want a different URL to open for each image button you could set it at runtime as follows
public void onCreate(Bundle b) {
Button button =(Button)findViewById(R.id.button);
button.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
//different action for each activity
}
});
}
Yes you can! I had multiple activities inflate the same layout but they save different shared preferences.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.same_layout);
TextView urlDesc = (TextView)findViewById(R.id.descriptionviewer);
urlDesc.setText("url_1"); //now in other activities-- urlDesc.setText("url_2");
ImageButton aButton = (ImageButton)findViewById(R.id.videolink);
aButton.setOnClickListener(aButtonListener);
}
private OnClickListener aButtonListener = new OnClickListener() {
public void onClick(View v) {
// go open url_1 here. In other activities, open url_x, url_y, url_z
finish();
}
};
Same code just swapping the text you want to set for the TextView and url to open in OnClickListener(). No more to change.