Checking dimensions of View in MainActivity - java

I want to get dimensions of View defined in activity_main.xml to create a Bitmap based on height and width. Writing them in textView shows 0 or stops app. I checked if this view is null, but it's not. Can I get dimensions of view this way?
Here is the code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.textView);
View view = (View) findViewById(R.id.view);
int x = view.getMeasuredWidth();
int y = view.getHeight();
int radius = 10;
textView.setText(view.getHeight());
// if (view == null) textView.setText("View is null");
// else textView.setText("View is not null");
Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Here is actvity_main.xml:
<View
android:id="#+id/view"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_marginLeft="44dp"
android:layout_marginStart="44dp"
android:layout_marginTop="136dp"
android:background="#color/green"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/textView"
android:layout_width="209dp"
android:layout_height="48dp"
android:layout_marginLeft="44dp"
android:layout_marginStart="44dp"
android:layout_marginTop="28dp"
android:text="TextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

Your are trying to set integer value in TextView in this line textView.setText(view.getHeight()); Try Convert it in String an then set as follows.
textView.setText(String.valueOf(view.getHeight()));
for getting view either use above line in onResume or use as follows
textView.post(new Runnable()
{
public void run()
{
textView.setText(String.valueOf(view.getHeight()));
}
});
I hope it's work for you
Thank You

getWidth and height will return 0 as long as the view has not been layedout.
in order to get the dimensions at the right moment you have 2 options:
add a ViewTreeObserver in onCreate and remove it when it gets called.
2.(i think is easier) add a runnable to the view as i have showed at the bottom:
view.post(new Runnable() {
#Override
public void run() {
int x = view.getMeasuredWidth();
int y = view.getHeight();
}
})
the post() method callback gets called only after the view inflating is done.
happy coding

You can try this solution
int height;
int width;
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
viewTreeObserver .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override public void onGlobalLayout () {
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
height = view.getLayoutParams().height;
width = view.getLayoutParams().width;
}
});

Just use this code :)
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
Now you can use height and width variable in your project

Related

Android GridView not showing images

I have made a GridView with an ImageAdapter but it does not show the images inside.
I tried to change numColumns, columnWidth and other attributes but it didn't work.
In Android Studio xml Design panel i can see my Gridview.
This is my gridview inside my xml layout file:
<GridView
android:id="#+id/gridview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/linear_buttons"
android:layout_above="#+id/btnSearch"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:background="#null"
android:columnWidth="120dp"
android:gravity="center"
android:numColumns="auto_fit"
android:stretchMode="columnWidth" />
This is my Adapter:
public class CustomGridViewAdapter extends BaseAdapter {
private final Context mContext;
public CustomGridViewAdapter(Context c) {
mContext = c;
}
public int getCount() {
return mThumbIds.length;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(R.dimen.grid_dimens_width, R.dimen.grid_dimens_height));
imageView.setScaleType(ImageView.ScaleType.CENTER);
imageView.setPadding(1, 5, 1, 1);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(mThumbIds[position]);
return imageView;
}
private final Integer[] mThumbIds = {
R.drawable.grid_agapis, R.drawable.grid_asteies, R.drawable.grid_auto,
R.drawable.grid_gamos, R.drawable.grid_goneis,
};
I set the adapter with the following code:
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new CustomGridViewAdapter(this));
gridview.setOnItemClickListener(this);
Can you explain me where is the problem?
Thank you.
imageView.setLayoutParams(new GridView.LayoutParams(R.dimen.grid_dimens_width,
R.dimen.grid_dimens_height));
With this line of code you are trying to limit the size of the grabbed drawable to a fixed width & height that are equal to grid_dimens_width & grid_dimens_height respectively.
But actually using R.dimen.foo won't return the value of foo, instead it returns the generated integer value of the resource itself which can be something like a big number (e.g. -21893103 or 33238590) .. this will make you see nothing on the screen because the image is either:
Too big (in case of a positive resource value 33238590) so you are seeing the tiny pixels of it
or too small (in case of a negative resource value -21893103) because its size is zero.
What you need to do instead is to get the dimen resource using getDimention() and pass the resource id to it.
To apply that to your code:
Replace:
imageView.setLayoutParams(new GridView.LayoutParams(R.dimen.grid_dimens_width,
R.dimen.grid_dimens_height));
With:
imageView.setLayoutParams(new GridView.LayoutParams(
(int) mContext.getResources().getDimension(R.dimen.grid_dimens_width),
(int) mContext.getResources().getDimension(R.dimen.grid_dimens_height)));
Result:

How to animate view to slide down and push down everything below it?

So I have looked into how to animate fade and drop down/slide up animations of Views using this thread, however it didn't quite work as expected. To begin with, here is the code I use for the animating:
public void toggleAdvancedVisibility(View text) { //text is a clickable textview thats acts as a toggle
int dur = 1000;
final View advView = findViewById(R.id.enc_advanced);
if(advView.getVisibility() == View.GONE && animationDone) {
advView.setVisibility(View.VISIBLE);
advView.setAlpha(0.0f);
//animate fade + drop down
advView.animate()
.setDuration(dur)
.translationY(advView.getHeight())
.alpha(1.0f)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
animationDone = true;
}
});
animationDone=false;
}
else if(advView.getVisibility() == View.VISIBLE && animationDone) {
//animate fade + slide up
advView.animate()
.setDuration(dur)
.translationY(0)
.alpha(0.0f)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
advView.setVisibility(View.GONE);
animationDone = true;
}
});
animationDone = false;
}
}
As I said, while there was animation, it didn't act anywhere near as expected.
Problem #1
The view is almost pushed out of visibility. I believe that this is due to the line .translationY(advView.getHeight()) as if I set the location of the view before the animation to advView.setTranslationY(-advView.getHeight()) and then animate .translationY(0) it goes to where it is supposed to.
The obvious problem with this is that while the view is animating, the view "collides" with the view above it before it is done. So how do I properly get this to slide down/up without running into the view above it?
Problem #2
The animation doesn't exactly "push" the view down, which is what I expected. What I mean by this is that the view being animated also has a view below it. I expected the view below it to be pushed down with the animated view. While I haven't tried it yet, I assume this can be simulated by setting the same animation to the view below it, but is there another way of doing it?
I am very new to this animation stuff and manipulating Views like this so any help is appreciated.
I made you a short example and I see it pushes down the rest of the view.
xml:
<LinearLayout 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.teststuff.MainActivity"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/b1"
android:text="show"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tv1"
android:text="Hello World!"
android:visibility="gone"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!!!!"/>
and here is the .java:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv1 = (TextView) findViewById(R.id.tv1);
tv2 = (TextView) findViewById(R.id.tv2);
b1 = (Button) findViewById(R.id.b1);
b1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (tv1.isShown()){
tv1.startAnimation(slideInAnimation(view));
tv1.setVisibility(View.GONE);
}
else {
tv1.startAnimation(slideOutAnimation(view));
tv1.setVisibility(View.VISIBLE);
}
}
});
}
private TranslateAnimation slideOutAnimation(View view){
TranslateAnimation animate = new TranslateAnimation(0,0,-view.getHeight(),0);
animate.setDuration(500);
animate.setFillAfter(false);
return animate;
}
private TranslateAnimation slideInAnimation(View view){
TranslateAnimation animate = new TranslateAnimation(0,0,0,-view.getHeight());
animate.setDuration(500);
animate.setFillAfter(true);
return animate;
}
It works fine for me.

I want to be able to get the screen height and width in Android

I am trying to get the screen width and height so that I can make a button move randomly on the screen when I click it.
The code I have so far it:
public class MainActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
DisplayMetrics display = this.getResources().getDisplayMetrics();
int width = display.widthPixels;
int height = display.heightPixels;
public void moveMe(View view)
{
Button myButton = (Button) findViewById(R.id.my_button);
Random r = new Random();
int x = (r.nextInt(width));
int y = (r.nextInt(height));
myButton.setX(x);
myButton.setY(y);
}
}
The XML is:
<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button
android:id="#+id/my_button"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_x="0dp"
android:layout_y="0dp"
android:text="Yes"
android:onClick="moveMe"/>
</AbsoluteLayout>
However, when I launch the app it says that it has stopped.
The error I get in Android Studio is:
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.test.test/com.example.test.test.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
Thankyou for any help you can offer.
You are calling getResources() before the super.onCreate(Bundle) is called. You must call getResources() after onCreate(). Move your code to onCreate or onStart:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DisplayMetrics display = getResources().getDisplayMetrics();
int width = display.widthPixels;
int height = display.heightPixels;
}
Try this inside onCreate:
DisplayMetrics display = this.getResources().getDisplayMetrics();
int width = display.widthPixels;
int height = display.heightPixels
You can use code so that no need to weight till your layout get rendered completely.
Display display = context.getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int height = size.y;
int width = size.x;
But in practice AbsoluteLayout is deprecated and its not good way to position Children using absolute positioning may not give same User Interface feel in all devices.Other option suggested by experts is RelativeLayout.
You can find downside of the AbsoluteLayout at the link Absolute positioning pitfalls

Updating TextView within Fragment for Consistency

I am trying to build a quiz app that keeps score as the user answers questions. I'm also using Google's ViewPager for screen slides which employs Fragments to keep different individual pages (code: http://developer.android.com/training/animation/screen-slide.html). I have a TextView (#id/txtvw_score) that is designed to keep score across the entire quiz session. This txtvw_score is placed within fragment_screen_slide_page.xml.
The problem I'm having is that, in order to access said TextView, I have to do it in ScreenSlidePageFragment's onCreateView() method. But, because Android calls this onCreateView() method twice for every new Fragment, it makes it so that the TextView's score is completely outdated. For example, page 1 would display the current score, then page 2 would display page 0 (uninitialized score), then page 3 would display page 1's score, etc.
I'm using SharedPreferences to keep the actual score updated. I've been able to keep track of the score perfectly fine when I use the ActionBar. This is only for confirmation purposes though, as I don't actually want to use the ActionBar.
Further complicating the problem is that I can't update the TextView within the Fragment's onCreate() method. I've also tried calling the TextView and changing it from ScreenSlideActivity (source: How to change fragment's textView's text from activity), but that ended up as NullPointerException no matter what I did.
So, long story short, how can I keep a TextView's content consistent across all Fragments? (This is a significant point for me, because, in the future, I want to use a TextView as a display for a timer as well, and that definitely needs to stay consistent).
EDIT (added simplified code):
fragment_screen_slide_page.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ScrollView
android:id="#+id/scrlvw_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="15dp" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp" >
<TextView
android:id="#+id/txtvw_score"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:textStyle="bold" />
</RelativeLayout>
</LinearLayout>
</ScrollView>
</RelativeLayout>
ScreenSlidePageFragment.java
public class ScreenSlidePageFragment extends Fragment
{
//The argument key for the page number this fragment represents.
public static final String ARG_PAGE = "page";
ArrayList<String> argAnswerList = new ArrayList<String>();
//The fragment's page number, which is set to the argument value for {#link #ARG_PAGE}.
private int mPageNumber;
//preferences
SharedPreferences sharedPrefs;
SharedPreferences preferences;
SharedPreferences.Editor editor;
final int DEFAULT = 0;
final int CHOSEN1 = 1;
final int CHOSEN2 = 2;
final int CHOSEN3 = 3;
final int CHOSEN4 = 4;
TextView txtvwScore;
int questionsTotal;
int numChosenCorrect, numChosenTotal, ratio;
//Factory method for this fragment class. Constructs a new fragment for the given page number.
public static ScreenSlidePageFragment create(int pageNumber)
{
ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
Bundle args = new Bundle();
args.putInt(ARG_PAGE, pageNumber);
fragment.setArguments(args);
return fragment;
}
public ScreenSlidePageFragment()
{
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getActivity().getActionBar().show();
fillArgAnswerList();
//fillAnswerList();
mPageNumber = getArguments().getInt(ARG_PAGE);
getPrefs();
preferences = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
editor = preferences.edit();
numChosenCorrect = sharedPrefs.getInt("numChosenCorrect", DEFAULT);
numChosenTotal = sharedPrefs.getInt("numChosenTotal", DEFAULT);
ratio = sharedPrefs.getInt("ratio", DEFAULT);
getActivity().getActionBar().setTitle("Score: " + numChosenCorrect + "/" + numChosenTotal + " (" + ratio + "%)");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
//Inflate the layout containing a title and body text.
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_screen_slide_page, container, false);
//setting the score
txtvwScore = (TextView) rootView.findViewById(R.id.txtvw_score);
setButtonOnClickListener();
return rootView;
}
private void setButtonOnClickListener()
{
button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
numChosenCorrect = sharedPrefs.getInt("numChosenCorrect", DEFAULT);
numChosenTotal = sharedPrefs.getInt("numChosenTotal", DEFAULT);
ratio = sharedPrefs.getInt("ratio", DEFAULT);
String answer = button.getText().toString();
if(answer.equals(correct))
{
numChosenCorrect++;
numChosenTotal++;
}
else
numChosenTotal++;
//update the actionBar score
ratio = Math.round(((float) numChosenCorrect)/((float) numChosenTotal)*100);
//setting the score
getActivity().getActionBar().setTitle("Score: " + numChosenCorrect + "/" + numChosenTotal + " (" + ratio + "%)");
editor.putInt("numChosenCorrect", numChosenCorrect);
editor.putInt("numChosenTotal", numChosenTotal);
editor.putInt("ratio", ratio);
editor.commit();
}
});
}
/**
* Get the preferences saved.
*/
private void getPrefs()
{
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
}
/**
* Returns the page number represented by this fragment object.
*/
public int getPageNumber()
{
return mPageNumber;
}
}
All codes have been simplified to show what the question revolves around. All removed code has been tested and they work up until this point.

Android View getWidth method always returns 0

I have a GridView to put some images in it. What I would like to do is to have the measurements of the GridView such as width and height so that I know what should be the optimal size of images when they are being showed in the getView() method. I want to show only 8 image per row. So say if a device has bigger screen the images will have bigger size instead of adding more image in the row by setting a fixed size for images.
So in the onCreate() method I initialize my custom Adapter and pass the getWidth and getHeight values into it. But they are always zero.
In the xml layout file, gridview was the only view, then I added it to a linearlayout so maybe it atleast return the width and height of its parent...but that is still zero.
Here is the onCreate method of the Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
if (savedInstanceState == null) {
}
int difficulty = getIntent().getExtras()
.getInt(AppConstants.EXTRAS_GAME_DIFFICULTY_LEVEL, 1);
LinearLayout lvg = (LinearLayout) findViewById(R.id.linearForGridGame);
GridView gv = (GridView) findViewById(R.id.gridview);
gv.setAdapter(new CellAdapter(this, difficulty, lvg.getWidth(), lvg.getHeight()));
gv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Toast.makeText(GameActivity.this, "" + position, Toast.LENGTH_SHORT).show();
}
});
}
Here is the xml layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linearForGridGame">
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="128px"
android:numColumns="auto_fit"
android:verticalSpacing="0dp"
android:horizontalSpacing="0dp"
android:stretchMode="none"
android:gravity="center"
android:choiceMode="none"
android:listSelector="#null"
android:clickable="true" />
</LinearLayout>
And here is the cosntructor of the adapter, where I get width and height always 0:
public CellAdapter(Context context, int difficultyLevel, int parentWidth, int parentHeight)
{
_context = context;
_dificultyLevel = difficultyLevel;
_parentHight = parentHeight;
_parentWidth = parentWidth;
Log.d(TAG, "Received difficulty level " + _dificultyLevel); //OK
Log.d(TAG, "Received parent width " + _parentWidth); //Always 0
Log.d(TAG, "Received parent height " + _parentHight); //Always 0
_cellWidth = (_parentWidth / 6); //Width of image to fill 6 per row
setupGame(_dificultyLevel);
}
You must wait until the view hierarchy is inflated and measured to know the dimensions. Add something like that in onCreate()
final ViewTreeObserver vto = lvg.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (lvg.getWidth() > 10){ // because it may be called before the view is measured and you will still get 0
// here you can get the measured dimensions
ViewTreeObserver obs = pictureImg.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this); // otherwise you're gonne keep getting called
}
}
});
Try doing lvg.getWidth() in onWindowFocusChanged(boolean ) of the activity
I think you have to use LayoutParams, like this:
...
LinearLayout lvg = (LinearLayout) findViewById(R.id.linearForGridGame);
ViewGroup.LayoutParams layoutParams = lvg.getLayoutParams();
GridView gv = (GridView) findViewById(R.id.gridview);
gv.setAdapter(new CellAdapter(this, difficulty, layoutParams.getWidth(), layoutParams.getHeight()));
...
Be sure to import android.view.ViewGroup.LayoutParams .
View layout hasn't occurred at onCreate(), you can see for yourself by subclassing View and observing when onLayout() is called.
Lots of info in this question. A ViewTreeObserver.OnGlobalLayoutListener may be more specific to your desire than onWindowFocusChanged(boolean), as you can target a particular view.
Example from this answer:
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Ensure you call it only once :
yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
// Here you can get the size :)
}
});

Categories