Gridlayout with 2x2 imageview android - java

I,ve got a gridview that gets 4 imageviews from an adapter, i want to have them 2x2 resized and fitted on my screen.
here´s the code for that:
#Override
protected View createQuizContentView() {
mAnswerView = new GridView(getContext());
StateListDrawable selector = new StateListDrawable();
selector.addState(new int[]{android.R.attr.state_pressed}, new ColorDrawable(Color.LTGRAY));
selector.addState(new int[]{-android.R.attr.state_pressed}, new ColorDrawable(Color.WHITE));
mAnswerView.setSelector(selector);
//mAnswerView.setSelector(R.drawable.selector_button);
mAnswerView.setNumColumns(2);
mAnswerView.setAdapter(new OptionsQuizAdapter(getQuiz().getOptions(),
R.layout.imgitem_answer));
mAnswerView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mAnswered = position;
allowAnswers(mAnswerView,position);
}
});
return mAnswerView;
}
and
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
convertView = inflater.inflate(mLayoutId, parent, false);
}
final ImageView imageView;
String text = getText(position);
Context context = parent.getContext();
Resources resources = context.getResources();
String stripped = text.replace("-", "");
final int resourceId = resources.getIdentifier(stripped, "drawable",
context.getPackageName());
Drawable d = ContextCompat.getDrawable(context, resourceId);
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int height = size.y;
double dviewheight = height/3.32;
int viewheight = (int)dviewheight;
imageView = (ImageView) convertView;
imageView.setPadding(64, 64, 64, 64);
convertView.setLayoutParams(newGridView.LayoutParams(GridView.AUTO_FIT, viewheight));
imageView.setImageDrawable(d);
return imageView;
}
At the moment i´am setting the height of the imageview inside the gridview by getting the screensize and dividing it which works fine on most device resolution but i still would prefer a more dynamic solution that would work if i added more rows to the gridview.
This is how it looks:
Screensize/magic number
When i use GridView.AUTO_FIT on the height it will add a lot of useless space and i get a scrollbar on the right like this:
Useless space vertically
I have tried several configurations and stretches but none seems to work. Any way to achieve what i want programmatically without xml?

Try this:
gridView.setNumColumns(2);

Related

How to get ImageView ID from inflater in Android

I'm using an inflater to show a popup setting menu made by me. It's composed by some images and buttons. After complete XML layout I'm started to code it, it opens correctly with this code:
public void Settings_button(View view)
{
if (p != null)
showPopup(Main_activity.this, p);
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
int[] location = new int[2];
ImageButton setting_button = (ImageButton) findViewById(R.id.settings_button);
// Get the x, y location and store it in the location[] array
// location[0] = x, location[1] = y.
setting_button.getLocationOnScreen(location);
//Initialize the Point with x, and y positions
p = new Point();
p.x = location[0];
p.y = location[1];
}
// The method that displays the popup.
private void showPopup(final Activity context, Point p) {
Resources r = getResources();
int popupWidth = 500;
int popupHeight = 300;
// Inflate the popup_layout.xml
LinearLayout viewGroup = (LinearLayout) context.findViewById(R.id.popup);
LayoutInflater layoutInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = layoutInflater.inflate(R.layout.settings_popup_layout, viewGroup);
// Creating the PopupWindow
final PopupWindow popup = new PopupWindow(context);
popup.setContentView(layout);
popup.setWidth(popupWidth);
popup.setHeight(popupHeight);
popup.setFocusable(true);
// Some offset to align the popup a bit to the right, and a bit down, relative to button's position.
int OFFSET_X = 150;
int OFFSET_Y = 30;
// Displaying the popup at the specified location, + offsets.
popup.showAtLocation(layout, Gravity.NO_GRAVITY, p.x + OFFSET_X, p.y + OFFSET_Y);
}
But now I need to get ImageViews IDs and Buttons IDs, I've used:
//In onCreate of Main_Activity
ImageView popup_setting_1_icon;
popup_setting_1_icon = (ImageView) findViewById(R.id.setting_1_icon);
Button popup_setting_1_button;
popup_setting_1_button = (Button) findViewById(R.id.setting_1_button);
But when I'm starting to use this Views for example popup_setting_1_icon.setImageResource(R.id.setting_done_icon); I get application crash with NullPointerException.
I've read something like I should get pointers from inflater but I've tried to do so inside showPopup method but nothing. How I can solve this problem?
Initialize views in show popup method:
ImageView popup_setting_1_icon;
popup_setting_1_icon = (ImageView)layout. findViewById(R.id.setting_1_icon);
Button popup_setting_1_button;
popup_setting_1_button = (Button)layout. findViewById(R.id.setting_1_button);
I had a very similar problem with programmatically inflating a container and populating it. This is what worked for me:
LayoutInflater inflator = LayoutInflater.from(getContext());
LinearLayout viewGroup = (LinearLayout) getActivity().findViewById(
R.id.popup);
View layout = inflator.inflate(R.layout.settings_popup_layout, null);
Button button = (Button) layout.findViewById(R.id.setting_1_button);
ImageView image = (ImageView) layout.findViewById(R.id.setting_1_icon);
//bind data to view/buttons
viewGroup.addView(layout);
This is assuming your settings button and image views are inside the settings popup layout.

How to change background of non-visible buttons in GridView (Button adapter) from an activity

My code works only for visible views.
mGridView = (GridView) findViewById(R.id.gridView);
mGridView.setAdapter(new ButtonAdapter(this, list));
Method called when chronometer ticks:
public void setBackground(int i) {
Button button = (Button) mGridView.getChildAt(i);
button.setBackgroundResource(R.drawable.button_shape);
This method causes NPE because getChildAt cannot access non-visible childs.
I tried some solutions found here but no luck so far (android - listview get item view by position - this solution did not causes NPE but was changing background for more buttons in one tick)
What I need is to change first button background on first tick, second button on second tick.. and last button on last tick and stay consistent while scrolling.
My getView method in ButtonAdapter:
public View getView(final int position,
View convertView, ViewGroup parent) {
Button btn;
LayoutInflater inflater = LayoutInflater.from(mContext);
if (convertView == null) {
// if it's not recycled, initialize some attributes
btn = (Button) inflater.inflate(R.layout.button, parent, false);
int width = mContext.getResources().getDimensionPixelSize(R.dimen.gridView_param_width);
int height = mContext.getResources().getDimensionPixelSize(R.dimen.gridView_param_height);
GridView.LayoutParams params = new GridView.LayoutParams(width, height);
btn.setLayoutParams(params);
} else {
btn = (Button) convertView;
}
btn.setText(list.get(position).getName());
btn.setId(position);
btn.setTag(position);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SetGridViewListener activity = (SetGridViewListener) mContext;
activity.onClickGridView(position);
Button btn = (Button)v;
btn.setBackgroundResource(R.drawable.button_shape_clicked);
btn.setClickable(false);
}
});
return btn;
}
I think my problem is in getView method which is probably not recycling well for my purpose.
Thanks in advance.
I have solved it already. I used ViewHolder pattern like here ListView subobject clickable confilct and this method to access buttons from my activity:
public View getViewByPosition(int pos, GridView gridView) {
final int firstListItemPosition = listView.getFirstVisiblePosition();
final int lastListItemPosition = firstListItemPosition + listView.getChildCount() - 1;
if (pos < firstListItemPosition || pos > lastListItemPosition ) {
return gridView.getAdapter().getView(pos, null, listView);
} else {
final int childIndex = pos - firstListItemPosition;
return gridView.getChildAt(childIndex);
}
}
(android - listview get item view by position)
If you would like my concrete solution, write in comment and I will add it.

Loading Image from URL in List View

I can download image and display it in ListView. But the problem I am facing is,
When I am loading the images they all get loaded in the first row of the list. It shows being loaded one by one in the first row. While the other rows hold the default image. Its look weird. What to do. The code below:
private class simpsync extends AsyncTask<String, Integer , Bitmap>{
private final WeakReference imageViewReference;
simpsync(ImageView iv){
//imageView=iv;
imageViewReference=new WeakReference(iv);
}
#Override
protected Bitmap doInBackground(String... param) {
Bitmap bmp=CommonFunctions.overlay(CommonFunctions.loadUrlBitmap(param[0]));
return bmp;
}
protected void onPostExecute(Bitmap bitmap) {
//imageView.setImageBitmap(result);
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = (ImageView) imageViewReference.get();
if (imageView != null) {
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
}
This code is the getView function of class BaseAdapter
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
ViewHolder holder;
if(convertView==null){
/****** Inflate tabitem.xml file for each row ( Defined below ) *******/
//list_book_detail_entry
if(requestType==SearchAndIndex.SEARCH_IN_SEPAERATE)
{
vi = inflater.inflate(R.layout.list_book_detail_buy, parent, false);
//vi = inflater.inflate(R.layout.list_book_detail_buy, null);
}
else{
vi = inflater.inflate(R.layout.list_book_detail_entry, parent, false);
//vi = inflater.inflate(R.layout.list_book_detail_entry, parent, false);
}
/****** View Holder Object to contain tabitem.xml file elements ******/
holder = new ViewHolder();
holder.bookTitle=(TextView)vi.findViewById(R.id.BookTitle);
holder.writer = (TextView) vi.findViewById(R.id.WriterName);
holder.imageUrl=(ImageView)vi.findViewById(R.id.ImageUrl);
holder.isbn=(TextView)vi.findViewById(R.id.BookISBN);
holder.serialNumber=(TextView)vi.findViewById(R.id.BookSerialNumber);
holder.availabilityView=(ImageView)vi.findViewById(R.id.AvailabilityView);
holder.publisher=(TextView)vi.findViewById(R.id.Publisher);
holder.publishingDate=(TextView)vi.findViewById(R.id.PublishingDate);
/************ Set holder with LayoutInflater ************/
vi.setTag( holder );
}
else
holder=(ViewHolder)vi.getTag();
if(data.size()<=0)
{
holder.bookTitle.setText("--");
holder.writer.setText("--");
holder.publisher.setText("--");
holder.publisher.setText("----+--+--");
}
else
{
tempValues=null;
tempValues = ( BookDetailsStruct ) data.get( position );
holder.writer.setText( tempValues.Writer );
holder.publisher.setText(tempValues.Publisher);
holder.publishingDate.setText(tempValues.getIssueDetail(0).publishingDate);
simpsync sp=new simpsync(holder.imageUrl);
if(requestType==SearchAndIndex.SEARCH_IN_SEPAERATE)
{
if(tempValues.getIssueDetail(0)!=null)
{
String toAdd;
if(tempValues.getIssueDetail(0).serialNumber==-1)
toAdd="";
else
toAdd=" [ 巻"+tempValues.getIssueDetail(0).serialNumber+" ]";
holder.bookTitle.setText( tempValues.BookName+toAdd);
sp.execute(tempValues.getIssueDetail(0).smallImageUrl);
}
}
else{
if(tempValues.largetNumberIndex!=-1)
{
String toAdd;
if(tempValues.getIssueDetail(tempValues.largetNumberIndex).serialNumber==-1)
toAdd="";
else
toAdd=" ("+tempValues.getIssueCount()+"巻)";
holder.bookTitle.setText( tempValues.BookName+toAdd);
sp.execute(tempValues.getIssueDetail(tempValues.largetNumberIndex).smallImageUrl);
}
else{
holder.bookTitle.setText( tempValues.BookName);
sp.execute(tempValues.getIssueDetail(0).smallImageUrl);
}
}
vi.setOnClickListener(new OnItemClickListener( position ));
}
return vi;
}
if you have any further question, please let me know
The "problem" is the recycle behavior of ListView. You are not respecting it enough. When you scroll down and a View disappears on the top, it will be reused at the bottom (thats why you use the ViewHolder pattern, thats good). But you also start a asynchronous task and give it the ImageView and to hold onto it. Since the whole view of the row (and with that the imageview) gets recycled, it wont be eligible for garbage collection, thus the asynctask has a valid ImageView to display the image once its finished.
To correct your code, I suggest you simply adapt what is written on the android developer page, it nearly is copy-past-ready code for you to use:
Load Bitmaps into a GridView Implementation
You can also use 3rd party libraries, because other smart people have also faced this problem and came up with good solutions:
Glide
Picasso
They both have a very (very) simple Api to get things done and they are both highly efficient and tunable, with already good settings by default.
You can use Picasso for loading images into ListView
#Override
public void getView(int position, View convertView, ViewGroup parent) {
SquaredImageView view = (SquaredImageView) convertView;
if (view == null) {
view = new SquaredImageView(context);
}
String url = getItem(position);
Picasso.with(context).load(url).into(view);
}
OR
#Override
public void getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
ImageView imgView = v.findViewById(R.id.someImageView);
String url = getItem(position);
Picasso.with(context).load(url ).into(imgView);
}
Many common pitfalls of image loading on Android are handled automatically by Picasso:
Handling ImageView recycling and download cancelation in an adapter.
Complex image transformations with minimal memory use.
Automatic memory and disk caching.

Android RecyclerView smooth scroll to view that's animating their height

I have a RecyclerView with Expandable Child Views, when the child ViewGroup is clicked it inflates an amount of views animating the ViewGroup height from 0 to the measured viewgroup height, like the following gif:
The problem is: I'm calling smoothScrollToPosition on recyclerView, it smooth scroll to the view position, but it considers the current view height, which is still not expanded, in the above gif i'm touching on the under view of the recyclerview, which dont scroll to position because the view is already visible, but when i touch again (calling the smoothscrolltoposition again) it scroll the view to the correct position, because the view is already expanded.
Is there any approach to scroll the view to the top of screen or just scroll to make content visible?
For references:
This is the method called to inflate the views:
collapsible_content.removeAllViews();
for(int i = 0; i < 5; i++) {
View link_view = getLayoutInflater().inflate(R.layout.list_item_timeline_step_link, collapsible_content, false);
TextView text = (TextView) link_view.findViewById(R.id.step_link_text);
text.setText("Test");
collapsible_content.addView(link_view);
}
And this is my method to expand:
public void toggle() {
collapsible_content.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
Animation a;
if (mExpanded) {
a = new ExpandAnimation(collapsible_content.getLayoutParams().height, 0);
} else {
a = new ExpandAnimation(collapsible_content.getLayoutParams().height, getMeasuredHeight());
}
a.setDuration(mAnimationDuration);
collapsible_content.startAnimation(a);
mExpanded = !mExpanded;
}
And the animation:
private class ExpandAnimation extends Animation {
private final int mStartHeight;
private final int mDeltaHeight;
public ExpandAnimation(int startHeight, int endHeight) {
mStartHeight = startHeight;
mDeltaHeight = endHeight - startHeight;
}
#Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
final int newHeight = (int) (mStartHeight + mDeltaHeight *
interpolatedTime);
collapsible_content getLayoutParams().height = newHeight;
if (newHeight <= 0) {
collapsible_content setVisibility(View.GONE);
} else {
collapsible_content setVisibility(View.VISIBLE);
}
collapsible_content requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
}
My solution was to constant check for view bottom within applyTransformation method, and compare it with RecyclerView height, if the bottom get higher than the RV height, i scroll by the diff values:
final int bottom = collapsible_content.getBottom();
final int listViewHeight = mRecyclerView.getHeight();
if (bottom > listViewHeight) {
final int top = collapsible_content.getTop();
if (top > 0) {
mRecyclerView.smoothScrollBy(0, Math.min(bottom - listViewHeight + mRecyclerView.getPaddingBottom(), top));
}
}
The trick was to use Math.min to get the view top, so it don't scroll up making the top not visible.
Solution based on ListViewAnimations
Add an animationlistener and start the scrolling of the recyclerview after the expanding animation is finished.

android.view.WindowManager$BadTokenException list view set text then focus

This is my code:
LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout viewGroup = (LinearLayout) findViewById(R.id.layout_popup);
final View layout = layoutInflater.inflate(R.layout.popup_layout,viewGroup);
final PopupWindow popup = new PopupWindow(GameActivity.this);
Point popupSize = new Point(getResources().getDimensionPixelSize(R.dimen.popup_width),getResources().getDimensionPixelSize(R.dimen.popup_height));
popup.setContentView(layout);
popup.setWidth(popupSize.x);
popup.setHeight(popupSize.y);
popup.setFocusable(true);
File dir = getFilesDir();
File temp = new File(dir.getPath()+"/temp.prj");
try {
if(!temp.exists())temp.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
File[] filesArray = dir.listFiles();
ArrayList<String> files = new ArrayList<>();
for(File file:filesArray)
{
files.add(file.getName());
}
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
popup.showAtLocation(layout, Gravity.NO_GRAVITY, (size.x-popupSize.x)/2, (size.y-popupSize.y)/2);
ListView listView = (ListView)layout.findViewById(R.id.listViewDir);
final EditText fileName = (EditText)layout.findViewById(R.id.editTextFilename);
final StableArrayAdapter adapter = new StableArrayAdapter(GameActivity.this,R.layout.list_item, files);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
final TextView item = (TextView) view;
fileName.setText(item.getText());
}
});
The bit that is causing me trouble is:
#Override
public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
final TextView item = (TextView) view;
fileName.setText(item.getText());
}
Where after I have clicked on the item in the list view, I then go to click on the filename view and the exception
android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.ViewRootImpl$W#53668b30 is not valid; is your activity running?
is thrown. I have tried running fileName.setText(item.getText()); on the UI thread, I have tried using MainActivity.this (or my variant of it, GameActivity.this) in numerous different places. Nothing seems to be working. Does anyone have any idea what's going wrong? All related questions seem to revolve around a Dialog rather than a Popup
UPDATE
For some unearthly reason, adding android:inputType="textNoSuggestions" to the EditText fileName in xml stops the exception. I only noticed this after prefixing the items in the listview with a number (which means they don't get spellchecked). It looks like someone else ran into a similar error Android ICS spelling error correction causes PopupWindow to crash and they too do not have an answer

Categories

Resources