I am trying to create a sliding tabs app like the one in the screenshot below although due to the lack of samples available, this one is the closest I could find for what I want to achieve.
I would be very grateful if anybody could help me with following:
Which Java code needs to be removed in order to safely get rid of the colour picker at the bottom + what codes needs to replace it so that I can use a hex colour of my choice for the Action bar, Indicator colour, Tab titles, Tab backgrounds and Tab separators.
Which code (Java and/or XML) needs to change so that I can use XML fragment files of my own rather than these 'cards'.
All help would be very much appreciated as there is literally no sample project out there that does what I want to achieve.
The link to the sample project that I intend to modify is below.
LINK: astuetz/PagerSlidingTabStrip · GitHub
Tabs
slidingTabs & my_tab_text_view
well for fragments of your own did you have a look at ViewPager? http://developer.android.com/training/animation/screen-slide.html
Also have a look at android sample code: https://developer.android.com/samples/SlidingTabsBasic/index.html
for controlling the actionBar: have a look at the new ToolBar api
https://chris.banes.me/2014/10/17/appcompat-v21/
after that if you still have questions you are welcome to ask
slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
slidingTabLayout.setDividerColors(getResources().getColor(android.R.color.transparent));
slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(android.R.color.white));
add this method to SlidingTabLayout
public class SlidingTabLayout extends HorizontalScrollView {
/**
* #####################Add THIS METHOD ############
*
* #param layoutResId Layout id to be inflated
* #param textViewId id of the {#link TextView} in the inflated view
*/
public void setCustomTabView (int layoutResId, int textViewId) {
mTabViewLayoutId = layoutResId;
mTabViewTextViewId = textViewId;
}
...
}
from your activity:
public void onCreate(){
slidingTabs.setCustomTabView(0, R.layout.my_tab_text_view)
}
where my_tab_text_view is a layout file:
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
just make sure that width is 0 and weight is 1
Related
This is my first time using Fragments and I'm rather new to Android. I'm using Fragments to manage a settings menu for my app.
I have a MainActivity with a FragmentContainerView, the XML is like so:
<androidx.fragment.app.FragmentContainerView
android:visibility="gone"
android:id="#+id/main_activity_fragment_container"
android:background="#color/white"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
In the MainActivity.java File I have a method assigned to a button to make the FragmentContainerView visible, like so:
private void showFragments(FragmentContainerView fragmentContainerView) {
fragmentContainerView.setVisibility(View.VISIBLE);
// There is more to this method, but I don't think it is necessary for this example
}
I'm able to navigate all around the settings menu: inflate fragments, replace fragments, update Shared Preferences, use Browser Intent, etc. - everything I need to do for a settings menu.
What I haven't been able to figure out is how to closeout the Settings Menu (close the FragmentContainerView in the MainActivity) after a selection has been made (and thus return to the game). I can create a "close" button in every fragment - but I can't figure out a method to close (setVisibility to GONE) the MainActivity's FragmentContainerView.
Once I'm in a Fragment - is it even possible to access the FragmentContainerView the fragment resides inside of? I've tried a number of ways to access it and set the visibility to GONE, but I just get a crash.
Inside a fragment:
FragmentContainerView mainActivityFragmentContainer = (FragmentContainerView) view.findViewById(R.id.main_activity_fragment_container);
public void closeFragment(FragmentContainerView mainActivityFragmentContainer) {
mainActivityFragmentContainer.setVisibility(View.GONE);
// This causes a crash, the error log states:
// NullPointerException: Attempt to invoke virtual method 'void androidx.fragment.app.FragmentContainerView.setVisibility(int)' on a null object reference
}
I know a workaround where I don't have the fragmentContainerView fill up the entire screen and leave space for an external close button - but this solution seems inefficient.Any advice would be helpful!
Yes you can do it,
1- Create a public function in your MainActivity that closes the fragment container:
public void closeFragment() {
fragmentContainerView.setVisibility(View.GONE);
}
2- Call that function from your fragment when you need it:
// getActivity() will return the activity linked to that fragment so this fragment should be linked MainActivity
MainActivity activity = (MainActivity) getActivity();
acitvity.closeFragment();
OK, I'm tearing my hair out.
I have a simple default relative layout XML for my main activity, and defined a LinearLayout in the XML as well. In my Java code, I added a LinearLayout row, orientation horizontal, and added it to the XML Linearlayout (which I found by ID). When I then added Buttons or Textviews to that layour row, they showed up perfectly.
However, when I attempted to do the same thing in another activity, I can't get the TextViews or Buttons to show up at all. I originally had a background image and tried adding my Buttons and TextViews directly to the root RelativeLayout (foundById), with plans to move them around by .setX and .setY, but I took away the background and reverted to referencing a specific Linearlayout like my main Activity (for testing purposes, to remove any anomalies)and it still won't show them. I have re-arranged and tested forever and can not see what I'm missing.
Here's my current XML (stripped down for testing):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_open_template"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.mystuff.stupidapp.OpenTemplateActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="#+id/openTemplateMain">
</LinearLayout>
</RelativeLayout>
...and here's the current code (also stripped down):
public class OpenTemplateActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_open_template);
final Resources res = getResources();
Intent intent = getIntent();
String fileName = intent.getStringExtra(MainActivity.EXTRA_FILENAME);
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int screenHeight = displaymetrics.heightPixels;
int screenWidth = displaymetrics.widthPixels;
LinearLayout.LayoutParams rowLayoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT, 1.0f);
LinearLayout llMain = new LinearLayout(this);
llMain.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout llMainParent = (LinearLayout) findViewById(R.id.openTemplateMain);
llMainParent.addView(llMain);
Button bTest = new Button(this);
bTest.setText("TESTB");
llMain.addView(bTest);
//other code below unrelated.
}
}
Ideas, anyone? Please?
Answering my own question for those with a similar issue:
Turns out the APK was stuck, but only with code. Meaning any changes to the XML template (adding/removing Text or buttons or backgrounds) would reflect on the test device when I hit Play or Debug. But any changes to the code would NOT be reflected. I noticed this when I changed the tags in my debug logging and they were not reflected in the logs. I also noticed a Toast-like popup saying something about the app being dismissed manually and to re-run via IDE, but it didn't stay around long enough for a good look.
Clearing the cache and data, the uninstalling the app, then re-launching via the IDE play, fixed the issue. Code changes are now being reflected as expected, and code-created views are being shown as well.
You can prevent this or something similar from happening in the future by disabling the instant run feature in Android Studio: https://stackoverflow.com/a/35169716/3662251
Instant run basically incrementally compiles your code and only pushes the changes to the device. Unfortunately in its current state inconsistencies between code and runtime happen quite often and can lead to many wasted hours of bug hunting when that bug was actually introduced by instant run.
Background: I have previously asked this question on the similar topic on how to pass an instance variable from a Java class to it corresponding layout file in Android development. It turns out there is no easy way to do this, so the following is a follow-up question finding a good way to let the xml file and the class talk to each other.
Question: I am developing an game (nearly complete in Java Swing, now turning it into an Android app). It contains multiple levels, each with a differenly sized chess board and chess pieces - for all intents and purposes, it is a chess puzzle app that displays a new puzzle on a differently sized board after the player has solved the previous puzzle. The business logic is complete but I'm working on the graphics. At the game start, a static class BoardFragment (contained by BoardContainer.class) with its corresponding layout xml file fragment_board.xml should display the board size (as GridLayout) and chess pieces corresponding to the first level, which then updates as the level is completed. For clarification, the code looks somewhat like this (skip to the end to see my actual problem):
//Here is BoardFragment.class, static class contained by BoardContainer:
public static class BoardFragment extends Fragment {
public BoardFragment() {
}
public int level=0;
public void buttonPressed(View view) {
//method that will advance the game and update "level"
//depending on which button was clicked
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_board,
container, false);
return rootView;
}
}
//This is fragment_board.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.exampletest.MainGame$BoardFragment" >
<android.support.v7.widget.GridLayout
xmlns:app="schemas.android.com/apk/res-auto"
android:id="#+id/gridView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:columnCount="4" >
<ImageButton
android:id="#+id/imageButton1"
android:layout_width="49dp"
android:layout_height="49dp"
android:contentDescription="#null"
android:onClick="buttonPressed"
android:src="#drawable/grid11" />
</android.support.v7.widget.GridLayout>
</RelativeLayout>
Note that for simplicity and space, only one image button is currently in the xml file, but with app:columnCount equaling 4, there would be 16. In fact, the number should depend on the "level" instance variable in the corresponding class (so that if level==5 then perhaps app:columnCount==6 and so forth) but I'm not sure how I would commmunicate that from the class file to the xml. Is it possible at all? In fact, when the level is completed, both the board size and number of pieces should change. Should this be done by
Having one xml fragment file for each level? Then the previous question is solved, but for many levels, it would lead to many fragment files - is that best practice?
Having one fragment xml file that updates as the level is completed?
I appreciate any help.
As mentioned in the other answers, you will need to create the layout for your board dynamically at runtime. However, you can still define your individual Views and their attributes in XML layout resources, thus retaining the advantages they provide of separating the non-essential parts of the layout from the code and allowing automatic resolution of configuration-specific layouts.
For example, your layout could be adapted to separate layout resources for the board and cell layouts:
fragment_board_grid.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.exampletest.MainGame$BoardFragment" >
<android.support.v7.widget.GridLayout
android:id="#+id/gridView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
fragment_board_cell.xml
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="49dp"
android:layout_height="49dp"
android:contentDescription="#null"
android:onClick="buttonPressed"
android:src="#drawable/grid11"
tools:context="com.example.exampletest.MainGame$BoardFragment" />
These can now be dynamically inflated to create a board:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_board_grid,
container, false);
GridLayout boardLayout = (GridLayout) rootView.findViewById(
R.id.gridView);
boardLayout.setColumnCount(columnsNum);
boardLayout.setRowCount(rowsNum);
for (int columnIndex = 0; columnIndex < columnsNum; columnIndex++) {
for (int rowIndex = 0; rowIndex < rowsNum; rowIndex++) {
ImageView cellView = (ImageView) inflater.inflate(
R.layout.fragment_board_cell, boardLayout, false);
// Here you can customize it, add listeners, etc.
boardLayout.addView(cellView);
}
}
return rootView;
}
Upon starting the next level you will need to reinflate the board layout from scratch. You can approach this in two ways:
Make each instance of your Fragment represent a particular board. This would make the Fragment logic extremely simple and focused on only one game level. The logic for starting a new level would be handled by it's Activity, by replacing the existing Fragment with a new instance. This would also have the advantage of making animations for starting a new level extremely easy to implement, as FragmentTransactions have inherent support for animations.
Have the Fragment handle everything. This could be achieved by creating a generic method that would inflate a new board layout on demand and delegate to it from the onCreateView() callback, then implementing a method that would remove the current Fragment layout from it's parent and replace it with a fresh layout generated by the board-inflating method. The layout root can be gotten from the getView() method (or in case of the support library it's first child, as the library wraps the initial Fragment layout in order to block the View state from being saved by the Activity).
A good way to handle your layouts dynamically is to generate them programmatically. It's not as easy as laying everything out in an XML file, but it will provide you with much more flexibility. Here is an example:
How to create a RelativeLayout programmatically with two buttons one on top of the other?
You could always build your views dynamically in code based upon the level rather than having your views be static in nature via use of the xml file
For example...
//the layout you will work with
GridLayout layout = (GridLayout) findViewById(R.id.gridLayout);
//properties for button
Button btn = new Button(this);
btn.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); //this will depend how you want the view to look
btn.setText("My button created programmatically");
btn.setId(someId);
//add button to the layout
layout.addView(btn);
if(level == whateverlevel){ //pseudo code here obviously
//make a new button/buttons in here like above and add it to your view depending on your level
}
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
Very simple question that doesn't need much code:
I'm using Android's default ActionBar (no Sherlock) in which I have a couple of MenuItems. One of them is a collapsible Action View (android:showAsAction="collapseActionView|always") for your typical search scenario: user clicks on the icon → a search box expands → when the user types anything onQueryTextChange does its magic).
SearchView sv = new SearchView(this);
sv.setLayoutParams(new ActionBar.LayoutParams(Gravity.RIGHT));
// Yada, yada...
sv.setOnQueryTextListener(new OnQueryTextListener() {
// The methods for onQueryTextSubmit and onQueryTextChange ... they apply filtering on the activity's list adapter and apparently work fine
}
menuItem.setActionView(sv); // menuItem is the item for the search action
searchAnswers.setOnActionExpandListener(new OnActionExpandListener() {
// The listener's methods for expand/collapse as I need some business logic behind them
}
All of the above works fine.
When the device is on portrait mode and has limited screen space, the SearchView occupies pretty much the whole action bar, hiding the other MenuItems (which have android:showAsAction="ifRoom") as well as the Activity's title, which is OK by me.
The problem is that if the device is on landscape mode (so that there's still free ActionBar space), the SearchView doesn't occupy the whole ActionBar (again, fine by me) but the Activity's title disappears (even though there's plenty of space where it could be displayed!). So my problem is that I get this empty space where the Activity's title could be shown.
Before expanding:
After expanding:
(Both screenshots are from a phone in landscape mode. Tested with Android 4.0.4 and 4.3.)
Any tips on how to keep the Activity's title when there's enough space?
Not sure if you found an answer to this problem yet. But I found an answer on this thread that may help - it certainly helped me.
ActionBar SearchView not fully expanding in landscape mode
Code copied from link:
searchView.setOnSearchClickListener(new OnClickListener() {
private boolean extended = false;
#Override
public void onClick(View v) {
if (!extended) {
extended = true;
LayoutParams lp = v.getLayoutParams();
lp.width = LayoutParams.MATCH_PARENT;
}
}
});
searchView.setMaxWidth(Integer.MAX_VALUE);
Building on CoolMind answer, it worked for me to set max width to a smaller value:
searchView.setMaxWidth(375);
setMaxWidth() interprets its parameter as pixels.