Super newbie question about accessing information in strings. I have chunked together an app that uses a fragmentpager and the PagerTabStrip.
It is supposed to display titles on the tab, but mine displays nothing...because I am a total newbie hacking his way through. I am so grateful for this community.
From my layout file that calls the content (and once I fix it the title--id#pager_header right?):
<android.support.v4.view.PagerTabStrip
android:id="#+id/pager_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textColor="#ffffff" />
My code that gets me the swipeable pages of content:
public class Poems extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contentpage);
/** Getting a reference to the ViewPager defined the layout file */
ViewPager pager = (ViewPager) findViewById(R.id.pager);
/** Getting fragment manager */
FragmentManager fm = getSupportFragmentManager();
/** Instantiating FragmentPagerAdapter */
MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);
/** Setting the pagerAdapter to the pager object */
pager.setAdapter(pagerAdapter);
}
}
My code that should get me the titles from the string reference file.
public class MyFragment extends Fragment{
int mCurrentPage;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/** Getting the arguments to the Bundle object */
Bundle data = getArguments();
/** Getting integer data of the key current_page from the bundle */
mCurrentPage = data.getInt("current_page", 0);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.myfragment_layout, container,false);
TextView tv = (TextView ) v.findViewById(R.id.tv);
tv.setMovementMethod(new ScrollingMovementMethod());
switch(mCurrentPage){
case 0:
tv.setText(" ");
break;
case 1:
tv.setText(R.string.content_1);
break;
case 2:
tv.setText(R.string.content_2);
break;
case 3:
tv.setText(R.string.content_3);
break;
}
return tv;
}
}
How do I get pager_header from this?
A sample relevant string is:
<string name="content_1">Welcome to this app!</string>
I am thinking that I need to better understand strings since I'm coming from more of a text content background.
How do I include in the stings.xml file the indicators that allow the app to reference the content associated with pageview so that I can have that show up in the tab? How I do make it so that the content shows "Welcome to this app" but the title in the tab shows "Home"?
I think as if each entry (page) is like a line in a database with one point of reference being the title, one being the content, one being a link or some other content. Each has its own id, right?
Thank you in advance for explaining this to a beginner.
You need to implement getPageTitle(int position) in MyFragmentPagerAdapter. The String that is returned from this method is used to set the title of the PagerTabStrip.
#Override
public CharSequence getPageTitle(int position) {
Log.v(TAG, "getPageTitle");
String tabTitle;
switch(position) {
case 0:
tabTitle = "Tab #0";
break;
case 1:
tabTitle = "Home";
break;
case 2:
tabTitle = "Tab #2";
break;
case 3:
tabTitle = "Tab #3";
break;
default:
tabTitle = "Default Tab Title";
break;
}
return tabTitle;
}
You can make this more dynamic by making the title and content arguments that you pass in using a Bundle by calling setArguments(Bundle args) on your MyFragments that you add to your ViewPager. In MyFragment in onCreateView() you can then set your content based on the passed in content String argument. Then in MyFragmentPagerAdapter you can get a reference to the Fragment at the position and pull get the title directly from MyFragment.
Related
I've been poking around the web (including SO), and have found lots of information on having persistent data. I've found strings, ints, doubles, calendars, just about everything. The ones I haven't seen a specific guide or tutorial for are booleans.
I've created an app which has a switch located on the main activity. I do not have a settings button, window, or even a pane, because the entire app is accessed from the main activity. I want to have the switch be persistent as well as the affects it holds (ie, when the app is closed, it will remember if the user disable or enable the vibrate function).
I have the switch to work where it does enable the vibration depending on which position the switch is in. I can't seem to get the switch to remain in the off position when the app is recreated.
Here's my code to save:
#Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_main);
dBell = MediaPlayer.create(this, R.raw.doorbell);
if ((bundle != null) && (bundle.getBoolean("vibetoggle") != false)) {
vibeOn = false;
} else {
vibeOn = true;
}
}
protected void onRestoreInstanceState(Bundle bundle) {
super.onRestoreInstanceState(bundle);
vibeOn = bundle.getBoolean("vibetoggle");
}
protected void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
bundle.putBoolean("vibetoggle", vibeOn);
}
public void onToggleClicked(View view) {
Vibrator vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
vibeOn = ((Switch) view).isChecked();
if (vibeOn) {
vibe.vibrate(100);
} else {
// No vibrate
}
}
public void playSound(View view) {
dBell.start();
if (vibeOn) {
Vibrator vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
vibe.vibrate(300);
} else {
// No vibrate
}
}
The shorter duration vibrate (100ms) is occurs when the switch is flipped from disabled to enabled. The longer one is what actually causes the button to vibrate when clicked.
I've kind of gotten the boolean logic to work, but the IO switch would still be set to default and wouldn't function correctly until it had been switched. The issue I have regarding the switch, is that I want the switch to be in the correct position when loaded (ie, if the boolean is saved false, then the switch will load in the off position). I can't figure out how to make the two communicate. I would assume the switch would have to change based on the boolean, rather than the other way around. I just can't figure out how to make the xml switch communicate with the one I have in java and visa versa.
Here's the xml code for the switch:
<Switch
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Vibration"
android:id="#+id/switch1"
android:layout_alignBottom="#+id/dBellButton"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:clickable="true"
android:checked="true"
android:onClick="onToggleClicked" />
I know the android:checked="true" causes the switch to be created in a default on position. I tried making a new boolean inside of the values xml (so rather than the code saying android:checked="true", it would say something along the lines of android:checked="#bool/vibeConvert), but found I didn't know how to edit the boolean through the java when it was there as well.
I've tried a few different methods to get this data to be persistent, but none of them worked for me. If I could get some help with persistent data, specifically regarding booleans, that would be fantastic.
EDIT: showing my attempt to use SharedPreferences.
protected void onRestoreInstanceState(Bundle bundle) {
super.onRestoreInstanceState(bundle);
vibeOn = bundle.getBoolean("vibetoggle");
// preferenceSettings = getPreferences(PREFERENCE_MODE_PRIVATE);
// vibeOn = preferenceSettings.getBoolean("on", true);
}
protected void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
bundle.putBoolean("vibetoggle", vibeOn);
// preferenceSettings = getPreferences(PREFERENCE_MODE_PRIVATE);
// preferenceEditor = preferenceSettings.edit();
// preferenceEditor.putBoolean("on", vibeOn);
// preferenceEditor.apply();
}
I'm not quite sure how how to use the SharedPreferences, and I couldn't find anywhere which specified where to place them. They are commented out here for obvious reasons. Not sure where to go from here.
Thanks!
Nathan
EDIT:
I tried using the following code:
public static final String PREFS_NAME = "MyPrefsFile";
MediaPlayer dBell;
boolean vibeOn;
#Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_main);
dBell = MediaPlayer.create(this, R.raw.doorbell);
// Restore preferences
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
boolean vibeSave = settings.getBoolean("vibeSave", vibeOn);
vibeOn = vibeSave;
Switch ("+id/switch1") = vibeSave;
}
#Override
protected void onStop() {
super.onStop();
// Let Editor make preference changes
// All objects are from android.context.Context
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("vibeSave", vibeOn);
editor.commit();
}
Here's my XML:
<Switch
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Vibration"
android:id="#+id/switch1"
android:layout_alignBottom="#+id/dBellButton"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:clickable="true"
android:checked="true"
android:onClick="onToggleClicked" />
I'm not sure if:
The boolean value is being saved.
The switch can be edited inside of the java code.
I've tried to play with the idea with the following line of code: Switch ("+id/switch1") = vibeSave;, but that didn't work. I'm not sure how to proceed from here. I want my XML to be able to start in the correct position based on the value in the boolean, but I'm not sure how to make my XML and my java talk to each other.
You can use SharedPreferences to store primitive values.
The best place to do this, is right after the value is changed.
So add the saving to the logic of your switch listener, and the loading to the logic of your switch initializer.
To get a SharedPreferences object for your application, use one of two
methods:
getSharedPreferences() - Use this if you need multiple preferences
files identified by name, which you specify with the first parameter.
getPreferences() - Use this if you need only one preferences file for
your Activity. Because this will be the only preferences file for your
Activity, you don't supply a name.
To write values:
Call edit() to get a SharedPreferences.Editor.
Add values with methods such as putBoolean() and putString().
Commit the new values with commit()
To read values, use SharedPreferences methods such as getBoolean() and getString().
The code in your onCreate should look something like this:
#Override
protected void onCreate(Bundle bundle) {
(...)
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
boolean vibeSave = settings.getBoolean("vibeSave", false);
Switch switch = (Switch) findViewById(R.id.switch1);
switch.setChecked(vibeSave);
}
I want to create button dynamically in my application. The buttons need to be created based on items fetched from database. What is the best way to achieve this. Should I go for grid layout or Linear layout. My layout is simple with max 3 buttons per row. Once the first row is complete the buttons should be placed in second row.
I scanned lot of similar questions(some had grid layout other were using Linear layout) but unable to decide what is the optimum way to implement this.
I am complete newbie in android application, so any code snippets would be really helpful. Apologies if someone feels this is a duplicate question (I searched a lot before posting but didn't find appropriate answer to layout to be used.)
Thanks.
Please try to use gridView same as bellow code.
// in xml write this code
<GridView
android:id="#+id/calendar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:numColumns="3" />
// grid adapter
public class GridAdapter extends BaseAdapter {
private final Context _context;
private final List<String> list;
public GridAdapter(Context context, ArrayList<String> list) {
super();
this._context = context;
this.list = list;
}
public String getItem(int position) {
return list.get(position);
}
#Override
public int getCount() {
return list.size();
}
public View getView(int position, View convertView, ViewGroup parent) {
Button button = new Button(_context);
button.setText("button" + list.get(position));
return button;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
}
/// in oncreate
gridView.setAdapter(new GridAdapter(getApplicationContext(),list);
I am trying to created a listview containing filenames. I want to set a additional information like file id with each list items, so when i click a filename, i have to get file id from it. please help me do this.
My sample code:
ListView listview = (ListView) findViewById(R.id.listview);
ArrayAdapter fileListAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, ['one','two','three']);
listview.setAdapter(fileListAdapter);
There are quite a few things involved here, so i'm providing you with an example of how you can achieve this (you can copy-paste and test):
public class MainActivity extends ListActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// First we simulate a pool of file names and IDs
String[] fileNames = {"fileName1", "fileName2", "fileName3"};
List<Integer> fileNameIds = new ArrayList<Integer>();
fileNameIds.add(1200);
fileNameIds.add(356);
fileNameIds.add(28);
// We call our custom Adapter
ArrayAdapter<String> adapter = new CustomAdapter(this, fileNames, fileNameIds);
// Finally we set the adapter to our list
setListAdapter(adapter);
}
// This is a custom adapter that uses ArrayAdapter for our purpose
// (as this is just an example you should consider using Base Adapter if you don't want
// to have a pool of filenames and a separate pool of ids)
class CustomAdapter extends ArrayAdapter<String>{
private final LayoutInflater INFLATER;
private final String[] FILE_NAMES;
private final List<Integer> FILE_NAME_IDS;
public CustomAdapter(Context context, String[] fileNames, List<Integer> fileNameIds) {
super(context, R.layout.custom_row, fileNames);
this.INFLATER = LayoutInflater.from(context);
this.FILE_NAMES = fileNames;
this.FILE_NAME_IDS = fileNameIds;
}
// HERE is where you can assign effectively an ID to your rows
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// this is an object that takes advantage of the holder pattern
// it retains the state of our rows in the list
FileNameHolder holder;
if(convertView == null){
convertView = INFLATER.inflate(R.layout.custom_row, null); // inflate your custom row
// now you need to assign specific identifier to the list row that the holder will retain
// for you, so you can always get this id by calling getTag from the View object on your
// item click listeners
holder = new FileNameHolder();
holder.fileName = (TextView) convertView; //since i only have a texView in layout i don't need to call findByView
convertView.setTag(holder); // relate the view to a custom FileNameHolder object that retains file name and its ID
} else{
holder = (FileNameHolder) convertView.getTag();
}
holder.fileName.setText(FILE_NAMES[position]); // PROVIDE the list with file name description
holder.idFileName = FILE_NAME_IDS.get(position); // ASSIGN file name ID
return convertView;
}
}
// This is an example of catching a row clicked and get the custom ID that you assigned,
// from here you can use that ID as you need
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// Here as you can see we obtain the object associated with the row that was clicked
FileNameHolder holder = (FileNameHolder) v.getTag();
// Here i provide a way you can test that you're always getting the correct file name and Id
Toast.makeText(this,
"File Name = " + holder.fileName.getText() +
", File ID = " + holder.idFileName,
Toast.LENGTH_SHORT).show();
}
// This is a class that takes advantage of the Holder Pattern and we use it to
// achieve what you need (remember this is just an example you should consider
// changing class and member access modifiers as you need)
class FileNameHolder{
Integer idFileName;
TextView fileName;
FileNameHolder() {
}
}
}
custom_row.xml is just a TextView (i took it from the simple_list_item_1 layout):
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:paddingStart="?android:attr/expandableListPreferredItemPaddingLeft"
android:textAppearance="?android:attr/textAppearanceMedium"
android:gravity="center_vertical"
/>
Hope it's useful... regards!!
I have Three Spinners= spinState,spinCounty,& spinCity, i would like to select the State spinner then choose a State,then the second spinner would give me the list of Counties within that particular state,then select the County,then the third spinner would give me a list of the cities with in that particular county,such as: (State)Florida,(County)Dade,(City)Miami then after all 3 have been selected pass that information to the next Activity/Class. Can anyone help? here is my code
Spinner spinState,spinCounty,spinCity;
Button bNext;
protected void onCreate(Bundle)
{
//TODO Auto generated method stub
super.oncreate(Bundle)
setContentView(R.layout.info);
Spinner States = (Spinner) findViewById(R.id.spinState);
ArrayAdapter USstates = ArrayAdapter.createFromResource(this,
R.array.States, android.R.layout.simple_spinner_item);
USstates.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
States.setAdapter(USstates);
Spinner Counties = (Spinner) findViewById(R.id.spinCounty);
ArrayAdapter UScounties = ArrayAdapter.createFromResource(this,
R.array.Counties, android.R.layout.simple_spinner_item);
UScounties.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Counties.setAdapter(UScounties);
Spinner Cities = (Spinner) findViewById(R.id.spinCity);
ArrayAdapter UScities = ArrayAdapter.createFromResource(this,
R.array.Cities,android.R.layout.simple_spinner_item);
UScities.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Cities.setAdapter(UScities);
initialize();
bNext.setOnClickListener(this);
}
What code should i use and where?PS. whoever may answer could u use my exact variables so i won't be confused,Thanks in Advance.
countries.setOnItemSelectedListener(new OnItemSelectedListener() {
ArrayAdapter<String> stateadapter=null;
#Override
public void onItemSelected(AdapterView<?> parent, View view,
int pos, long id) {
switch (pos) {
case 0:
stateadapter = new ArrayAdapter<String>(
activityclass.this,
android.R.layout.simple_spinner_item, Arrays
.asList(getResources().getStringArray(
R.array.USAstate)));
states.setAdapter(stateadapter);
case 1:
stateadapter = new ArrayAdapter<String>(
activityclass.this,
android.R.layout.simple_spinner_item, Arrays
.asList(getResources().getStringArray(
R.array.Indiastate)));
states.setAdapter(stateadapter);
}
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
});
}
Select State
<string-array name="USAstate">
<item>california</item>
<item>texas</item>
<item>virgina</item>
<item>alaska</item>
</string-array>
I'm trying to determine the real dimension in pixels of some UI elements !
Those elements are inflated from a .xml file and are initialized with dip width and height so that the GUI will eventually support multiple screen size and dpi (as recommended by android specs).
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="150dip"
android:orientation="vertical">
<ImageView
android:id="#+id/TlFrame"
android:layout_width="110dip"
android:layout_height="90dip"
android:src="#drawable/timeline_nodrawing"
android:layout_margin="0dip"
android:padding="0dip"/></LinearLayout>
This previous xml represent one frame. But I do add many dynamically inside a horizontal layout describe here :
<HorizontalScrollView
android:id="#+id/TlScroller"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_margin="0dip"
android:padding="0dip"
android:scrollbars="none"
android:fillViewport="false"
android:scrollbarFadeDuration="0"
android:scrollbarDefaultDelayBeforeFade="0"
android:fadingEdgeLength="0dip"
android:scaleType="centerInside">
<!-- HorizontalScrollView can only host one direct child -->
<LinearLayout
android:id="#+id/TimelineContent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_margin="0dip"
android:padding="0dip"
android:scaleType="centerInside"/>
</HorizontalScrollView >
The method defined to add one frame inside my java code :
private void addNewFrame()
{
LayoutInflater inflater = (LayoutInflater) _parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.tl_frame, null);
TextView frameNumber = (TextView) root.findViewById(R.id.FrameNumber);
Integer val = new Integer(_nFramesDisplayed+1); //+1 to display ids starting from one on the user side
frameNumber.setText(val.toString());
++_nFramesDisplayed;
_content.addView(root);
// _content variable is initialized like this in c_tor
// _content = (LinearLayout) _parent.findViewById(R.id.TimelineContent);
}
Then inside my code, I try to get the actual real size in pixel because I need this to draw some opengl stuff over it.
LayoutInflater inflater = (LayoutInflater) _parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.tl_frame, null);
ImageView frame = (ImageView) root.findViewById(R.id.TlFrame);
frame.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
frame.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
final int w = frame.getMeasuredWidth();
final int h = frame.getMeasuredHeight();
Everything seems to work fine except that those values are way bigger than the actual pixel size of the ImageView.
Reported infos from getWindowManager().getDefaultDisplay().getMetrics(metrics);
are the following :
density = 1,5
densityDpi = 240
widthPixel = 600
heightPixel = 1024
Now, I know the rule from android is : pixel = dip * (dpi /160). But nothing makes any sense with the value returned. For that ImageView of (90dip X 110dip), the returned values of the measure() method is (270 x 218) which I assumed is in pixel !
Anyone has any idea why ?
Is the value returned in pixel ?
By the way : I've been testing the same code but with a TextView instead than an ImageView and everything seems to be working fine ! Why !?!?
You're calling measure incorrectly.
measure takes MeasureSpec values which are specially packed by MeasureSpec.makeMeasureSpec. measure ignores LayoutParams. The parent doing the measuring is expected to create a MeasureSpec based on its own measurement and layout strategy and the child's LayoutParams.
If you want to measure the way that WRAP_CONTENT usually works in most layouts, call measure like this:
frame.measure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
If you don't have max values (for example if you're writing something like a ScrollView that has infinite space) you can use the UNSPECIFIED mode:
frame.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
Do that:
frame.measure(0, 0);
final int w = frame.getMeasuredWidth();
final int h = frame.getMeasuredHeight();
Solved!
Ok ! Kind of Answering my own question here...But not completly
1 - It seems that on some devices, The ImageView measuring do not provide with exact values. I've seen lots of reports on this happenning on Nexus and Galaxy devices for example.
2 - A work around that I've come up with :
Set the width and height of your ImageView to "wrap_content" inside xml code.
Inflate the layout inside your code (generally in the UI initialization I suppose).
LayoutInflater inflater = (LayoutInflater)
_parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.tl_frame, null);
ImageView frame = (ImageView) root.findViewById(R.id.TlFrame);
Calculate your own ratio for your image view, based on the typical Android calculation
//ScreenDpi can be acquired by getWindowManager().getDefaultDisplay().getMetrics(metrics);
pixelWidth = wantedDipSize * (ScreenDpi / 160)
Use the calculated size to set your ImageView dynamycally inside your code
frame.getLayoutParams().width = pixeWidth;
And voila ! your ImageView has now the wanted Dip size ;)
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
#Override
public void onGlobalLayout() {
//now we can retrieve the width and height
int width = view.getWidth();
int height = view.getHeight();
//this is an important step not to keep receiving callbacks:
//we should remove this listener
//I use the function to remove it based on the api level!
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN){
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}else{
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
});
One should go with How to get width/height of a View
Unfortunately, in Activity lifecycle methods such as Activity#onCreate(Bundle), a layout pass has not yet been performed, so you can't yet retrieve the size of views in your view hierarchy. However, you can explicitly ask Android to measure a view using View#measure(int, int).
As #adamp's answer points out, you have to provide View#measure(int, int) with MeasureSpec values, but it can be a bit daunting figuring out the correct MeasureSpec.
The following method tries to determine the correct MeasureSpec values and measures the passed in view:
public class ViewUtil {
public static void measure(#NonNull final View view) {
final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
final int horizontalMode;
final int horizontalSize;
switch (layoutParams.width) {
case ViewGroup.LayoutParams.MATCH_PARENT:
horizontalMode = View.MeasureSpec.EXACTLY;
if (view.getParent() instanceof LinearLayout
&& ((LinearLayout) view.getParent()).getOrientation() == LinearLayout.VERTICAL) {
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
horizontalSize = ((View) view.getParent()).getMeasuredWidth() - lp.leftMargin - lp.rightMargin;
} else {
horizontalSize = ((View) view.getParent()).getMeasuredWidth();
}
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
horizontalMode = View.MeasureSpec.UNSPECIFIED;
horizontalSize = 0;
break;
default:
horizontalMode = View.MeasureSpec.EXACTLY;
horizontalSize = layoutParams.width;
break;
}
final int horizontalMeasureSpec = View.MeasureSpec
.makeMeasureSpec(horizontalSize, horizontalMode);
final int verticalMode;
final int verticalSize;
switch (layoutParams.height) {
case ViewGroup.LayoutParams.MATCH_PARENT:
verticalMode = View.MeasureSpec.EXACTLY;
if (view.getParent() instanceof LinearLayout
&& ((LinearLayout) view.getParent()).getOrientation() == LinearLayout.HORIZONTAL) {
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
verticalSize = ((View) view.getParent()).getMeasuredHeight() - lp.topMargin - lp.bottomMargin;
} else {
verticalSize = ((View) view.getParent()).getMeasuredHeight();
}
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
verticalMode = View.MeasureSpec.UNSPECIFIED;
verticalSize = 0;
break;
default:
verticalMode = View.MeasureSpec.EXACTLY;
verticalSize = layoutParams.height;
break;
}
final int verticalMeasureSpec = View.MeasureSpec.makeMeasureSpec(verticalSize, verticalMode);
view.measure(horizontalMeasureSpec, verticalMeasureSpec);
}
}
Then you can simply call:
ViewUtil.measure(view);
int height = view.getMeasuredHeight();
int width = view.getMeasuredWidth();
Alternatively, as #Amit Yadav suggested, you can use OnGlobalLayoutListener to have a listener called after the layout pass has been performed. The following is a method that handles unregistering the listener and method naming changes across versions:
public class ViewUtil {
public static void captureGlobalLayout(#NonNull final View view,
#NonNull final ViewTreeObserver.OnGlobalLayoutListener listener) {
view.getViewTreeObserver()
.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
final ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
viewTreeObserver.removeOnGlobalLayoutListener(this);
} else {
//noinspection deprecation
viewTreeObserver.removeGlobalOnLayoutListener(this);
}
listener.onGlobalLayout();
}
});
}
}
Then you can:
ViewUtil.captureGlobalLayout(rootView, new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int width = view.getMeasureWidth();
int height = view.getMeasuredHeight();
}
});
Where rootView can be the root view of your view hierarchy and view can be any view within your hierarchy that you want to know the dimensions of.
You have to create Custom Textview and use it in your layouts and use getActual height function to set the height at runtime
public class TextViewHeightPlus extends TextView {
private static final String TAG = "TextViewHeightPlus";
private int actualHeight=0;
public int getActualHeight() {
return actualHeight;
}
public TextViewHeightPlus(Context context) {
super(context);
}
public TextViewHeightPlus(Context context, AttributeSet attrs) {
super(context, attrs);
setCustomFont(context, attrs);
}
public TextViewHeightPlus(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
actualHeight=0;
actualHeight=(int) ((getLineCount()-1)*getTextSize());
}
}
Probably, because of what you have in AndroidManifest.xml (link) file and from which drawable-XXX directory the xml file comes, Android loads resources with scaling operation. You decide to use "dip" (link) dimension unit which is virtual and the real value (px) can be different.