Android: How to preload views? - android

I am trying to make a game where in the start menu, when a button is clicked, a story event will be happening. There will be different stories when the button is clicked depending on the character the player is using,so I don't want to make a billion different activities/fragments for the stories.
I have two layouts that I want to switch within the same activity.
To switch from layout 1 to layout 2, I tried to add layout 2 to layout 1, and also tried ViewFlipper , but when I try to switch from Layout 1 to layout 2, the TextView in layout 2 says "New text" first before turning into what I want it to say. The textbox, which is supposed to be at the bottom, appears at the top first and then jumps to the bottom
I've also tried using threads to have layout 2 preload but that just messes up layout 2. So what should I do?
Here is my class for the story
public class Story extends FrameLayout {
String[][] story;
int H;
int W;
int L;
int index =0;
TextView text;
ImageView character;
View v;
Story(Context context,String[][]story,int length){
super(context);
this.story=story;
this.L = length;
init(context);
}
public void init(Context context) {
LayoutInflater l = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = l.inflate(R.layout.story, this, true);
final RelativeLayout.LayoutParams textParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
final FrameLayout.LayoutParams imageParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
text = (TextView) v.findViewById(R.id.story);
character = (ImageView) v.findViewById(R.id.chara);
final RelativeLayout textBox = (RelativeLayout) v.findViewById(R.id.textbox);
textBox.post(new Runnable() {
#Override
public void run() {
H = textBox.getHeight();
W = textBox.getWidth();
textParams.setMargins(W / 12, H / 8, W / 10, H / 8);
text.setLayoutParams(textParams);
text.setText(story[index][0]);
}
});
v.post(new Runnable() {
#Override
public void run() {
int h = v.getHeight();
imageParams.setMargins(W / 7 * 2, h / 10, 0, H / 3);
character.setLayoutParams(imageParams);
character.setImageResource(Integer.parseInt(story[index][1]));
}
});
}
}
XML for the story
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/chara"
android:scaleType="fitStart"
android:adjustViewBounds="true"
/>
<RelativeLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/textbox"
android:layout_gravity="bottom">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/vntextbox"
android:adjustViewBounds="true"
android:scaleType="fitEnd">
</ImageView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/story" />
</RelativeLayout>
</merge>
For each character, they have a method that returns their story, eg for a particular character :
public class Player extends ImageView {
Context context;
Player (Context context){
super(context);
this.context=context;
}
public Story getIntro(){
int normal = R.drawable.normal;
int serious = R.drawable.serious;
int happy = R.drawable.happy;
final int length = 8;
String[][] story = // a story
final Story s = new Story(context, story, l);
s.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//do some stuff
}
});
return s;
}
}
And here is where the story is used
public class StartMenu extends AppCompatActivity {
#Override
protected void onCreate(final Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.startmenu);
final RelativeLayout r = (RelativeLayout)findViewById(R.id.startscreen);
final Button b =new Button(this);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//here I want to do something like this: (doesn't work due to problems described above)
Player p = new Player(StartMenu.this);
Story s = p.getIntro();
r.removeAllViews();
r.addView(s);
}
});

Try to leave the text space empty on your .xml and just fill it when you're creating the activity (if it is dynamic). It will solve the text change flick.
About the two layouts, search about Fragments.

Related

How to put all action items on the left, taking as much space as possible, and yet have overflow on the right?

Background
Suppose I have a Toolbar, and multiple action items. Some might be customized (example: TextView with image).
What I need to do is to align them all to the left, instead of to the right, yet still have the overflow item on the right side.
I also try to have as much space as possible to the action items.
The problem
None of what I've found works
What I've tried
1.For the alignment, I've found some solutions on StackOverflow, of adding views inside the Toolbar, but this won't work well for some reason, because pressing an item doesn't show the effect on the whole item (as if it's smaller in height).
Other things I tried for this:
android:layoutDirection="ltr" - doesn't do anything to the action items
android:gravity="left|start" - same
2.For the space issue, none of what I tried work. I tried to remove all things that might add margins or padding.
Here's a sample code to show how I tested both issues :
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" tools:context="com.example.user.myapplication.MainActivity">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
android:layoutDirection="ltr" android:padding="0px" android:theme="#style/ThemeOverlay.AppCompat.ActionBar"
app:contentInsetEnd="0px" app:contentInsetEndWithActions="0px" app:contentInsetLeft="0px"
app:contentInsetRight="0px" app:contentInsetStart="0px" app:contentInsetStartWithNavigation="0px"
app:logo="#null" app:title="#null" app:titleMargin="0px" app:titleTextColor="#757575"
tools:ignore="UnusedAttribute" tools:title="toolbar"/>
</FrameLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar mainToolbar = findViewById(R.id.toolbar);
for (int i = 0; i < 10; ++i) {
final View menuItemView = LayoutInflater.from(this).inflate(R.layout.action_item, mainToolbar, false);
ImageView imageView = (ImageView) menuItemView.findViewById(android.R.id.icon);
String text = "item" + i;
final int itemIconResId = R.drawable.ic_launcher_background;
imageView.setImageResource(itemIconResId);
((TextView) menuItemView.findViewById(android.R.id.text1)).setText(text);
final OnClickListener onClickListener = new OnClickListener() {
#Override
public void onClick(final View view) {
//do something on click
}
};
menuItemView.setOnClickListener(onClickListener);
final MenuItem menuItem = mainToolbar.getMenu()
.add(text).setActionView(menuItemView).setIcon(itemIconResId)
.setOnMenuItemClickListener(new OnMenuItemClickListener() {
#SuppressLint("MissingPermission")
#Override
public boolean onMenuItemClick(final MenuItem menuItem) {
onClickListener.onClick(menuItemView);
return true;
}
});
MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
}
}
action_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content" android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground" android:clickable="true" android:focusable="true"
android:focusableInTouchMode="false" android:gravity="center" android:orientation="horizontal">
<ImageView
android:id="#android:id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:scaleType="center" tools:src="#android:drawable/sym_def_app_icon"/>
<TextView
android:id="#android:id/text1" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="6dp" android:layout_marginStart="6dp" android:gravity="center"
android:textColor="#c2555555" android:textSize="15sp" tools:text="text"/>
</LinearLayout>
This is what I got:
The question
How can I support max space usage of the Toolbar, and also make the action items align to the left?
EDIT: after a bit work, I got the alignment solution to partially work:
activity_main.xml
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent" android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
android:background="#fff" android:gravity="center_vertical|start"
android:layoutDirection="ltr" android:padding="0px" android:theme="#style/ThemeOverlay.AppCompat.ActionBar"
app:contentInsetEnd="0px" app:contentInsetEndWithActions="0px" app:contentInsetLeft="0px"
app:contentInsetRight="0px" app:contentInsetStart="0px" app:contentInsetStartWithNavigation="0px"
app:logo="#null" app:title="#null" app:titleMargin="0px" app:titleTextColor="#757575"
tools:ignore="UnusedAttribute" tools:title="toolbar">
<android.support.v7.widget.ActionMenuView
android:id="#+id/amvMenu" android:layout_width="match_parent" android:layout_height="match_parent"/>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
In code, the only difference is that I use the menu of ActionMenuView, instead of the Toolbar:
final ActionMenuView amvMenu = (ActionMenuView) toolbar.findViewById(R.id.amvMenu);
final Menu menu =amvMenu.getMenu();
...
final MenuItem menuItem = menu.add...
It does put the overflow item on the far right, while the action items are on the left.
However, the effect of pressing doesn't include the whole height of the items, and it seems as if the items take more space than usual. Plus, I still didn't figure out how to use all the possible space there is here:
EDIT:
In order to fix the issue of the pressing effect, all I had to do is to add android:minHeight="?attr/actionBarSize" to the items that are being inflated in the loop.
What's still weird about the pressing effect is that if I add a normal action item (just text/icon, without inflating), it has a tiny ripple effect, and the action item itself take a lot of space compared to what I add.
Another new issue that this has caused, is that clicking on anywhere near the overflow menu will trigger clicking on it.
EDIT:
Yet another issue from this solution, is that there are spaces between items in some cases, such as one in the case that there are only a few items:
So, in short, this solution doesn't work well at all.
So if I understand this correctly, you want to add some actions in Toolbar. These actions should start from left and take all the space that is available.
Are you open to using custom views (ImageView, etc) for actions instead of MenuItem?
Add a horizontal LinearLayout to your Toolbar. And set equal weight to all the children (actions).
<Toolbar>
<LinearLayout horizontal>
<ImageView layout_width="0dp" layout_weight="1" />
<ImageView layout_width="0dp" layout_weight="1" />
<ImageView layout_width="0dp" layout_weight="1" />
</LinearLayout>
</Toolbar>
You can now attach menu to get vertical 3 dots action. Or you can add another ImageView at the end of the horizontal layout with fixed width.
EDIT:
Here's a solution that I quickly came up with. You will of course need to refine the code a bit. This solution uses a custom LinearLayout which measures each child and decides if overflow menu will be required or not. It will remeasure each child again to give equal space to all.
It uses PopupWindow to show menu and simple OnClickListener and callback to check which menu item was clicked.
FlexibleMenuContainer
public class FlexibleMenuContainer extends LinearLayout {
private List<FlexibleMenu.MenuItem> items;
private List<FlexibleMenu.MenuItem> drawableItems;
private List<FlexibleMenu.MenuItem> overflowItems;
private List<FlexibleMenu.MenuItem> overflowItemsTempContainer;
private ImageView overflow;
private int overflowViewSize;
private boolean isOverflowing;
public FlexibleMenuContainer(Context context) {
this(context, null);
}
public FlexibleMenuContainer(Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public FlexibleMenuContainer(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, #Nullable AttributeSet attrs) {
setOrientation(HORIZONTAL);
items = new ArrayList<>();
overflowItems = new ArrayList<>();
drawableItems = new ArrayList<>();
overflowItemsTempContainer = new ArrayList<>();
overflowViewSize = getResources().getDimensionPixelOffset(R.dimen.menu_more_size);
overflow = new ImageView(context);
overflow.setImageResource(R.drawable.ic_more_vert_white_24dp);
overflow.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
showOverflowMenu();
}
});
// overflow.setVisibility(GONE);
LinearLayout.LayoutParams params = new LayoutParams(overflowViewSize, overflowViewSize);
params.gravity = Gravity.CENTER_VERTICAL;
addView(overflow, params);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthRequired = 0;
isOverflowing = false;
overflowItems.clear();
drawableItems.clear();
if (items.size() == 0) {
return;
}
int availableWidth = MeasureSpec.getSize(widthMeasureSpec) - overflowViewSize;
for (int i=0; i<items.size(); i++) {
View child = items.get(i).getView();
measureChild(child, widthMeasureSpec, heightMeasureSpec);
widthRequired += child.getMeasuredWidth();
if (widthRequired > availableWidth) {
isOverflowing = true;
overflowItems.add(items.get(i));
} else {
drawableItems.add(items.get(i));
}
}
int drawableWidth = MeasureSpec.getSize(widthMeasureSpec) - (isOverflowing ? overflowViewSize : 0);
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(drawableWidth/drawableItems.size(), MeasureSpec.EXACTLY);
for (int i=0; i<drawableItems.size(); i++) {
View child = drawableItems.get(i).getView();
child.measure(childWidthMeasureSpec, heightMeasureSpec);
}
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = 0;
for (int i=0; i<drawableItems.size(); i++) {
View child = drawableItems.get(i).getView();
int height = Math.min(child.getMeasuredHeight(), b - t);
int top = (b - t - height)/2;
child.layout(left, top, left + child.getMeasuredWidth(), top + height);
left += child.getMeasuredWidth();
}
if (isOverflowing) {
overflow.layout(getMeasuredWidth() - overflowViewSize, t, getMeasuredWidth(), b);
}
// After opening the menu and dismissing it, the views are still laid out
for (int i=0; i<overflowItems.size(); i++) {
View child = overflowItems.get(i).getView();
if (child.getParent() == this) {
child.layout(0, 0, 0, 0);
}
}
}
public void addItem(FlexibleMenu.MenuItem item) {
items.add(item);
_addView(item.getView());
}
private void _addView(View view) {
LinearLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_VERTICAL;
addView(view, getChildCount() - 1, params);
}
private void showOverflowMenu() {
if (overflowItems.size() == 0) {
return;
}
final ViewGroup contentView = prepareContentViewForPopup();
final PopupWindow popup = new PopupWindow(contentView, 400, 300, true);
popup.setOutsideTouchable(false);
popup.setFocusable(true);
popup.showAsDropDown(overflow);
popup.setOnDismissListener(new PopupWindow.OnDismissListener() {
#Override
public void onDismiss() {
contentView.removeAllViews();
for (int i=0; i<overflowItemsTempContainer.size(); i++) {
View view = overflowItemsTempContainer.get(i).getView();
_addView(view);
}
overflowItemsTempContainer.clear();
}
});
}
private ViewGroup prepareContentViewForPopup() {
overflowItemsTempContainer.clear();
LinearLayout layout = new LinearLayout(getContext());
layout.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
layout.setOrientation(VERTICAL);
for (int i=0; i<overflowItems.size(); i++) {
overflowItemsTempContainer.add(overflowItems.get(i));
View view = overflowItems.get(i).getView();
removeView(view);
layout.addView(view);
}
return layout;
}
}
FlexibleMenu
public class FlexibleMenu {
private final List<MenuItem> items;
private final MenuCallback callback;
public FlexibleMenu(List<MenuItem> items, MenuCallback callback) {
this.items = items;
this.callback = callback;
}
public void inflate(FlexibleMenuContainer container) {
for (int i=0; i<items.size(); i++) {
final MenuItem item = items.get(i);
container.addItem(item);
item.getView().setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
callback.onItemClicked(item);
}
});
}
}
public interface MenuCallback {
void onItemClicked(MenuItem item);
}
public static class MenuItem {
private final int id;
private final View view;
public MenuItem(int id, View view) {
this.id = id;
this.view = view;
}
public View getView() {
return view;
}
public int getId() {
return id;
}
}
}
layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.fenchtose.flexiblemenu.MainActivity">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="56dp"
android:paddingStart="0dp"
android:background="#color/colorPrimary">
<com.fenchtose.flexiblemenu.FlexibleMenuContainer
android:id="#+id/menu_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v7.widget.Toolbar>
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="56dp"
android:paddingStart="0dp"
android:background="#color/colorPrimary">
<com.fenchtose.flexiblemenu.FlexibleMenuContainer
android:id="#+id/menu_container1"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v7.widget.Toolbar>
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="56dp"
android:paddingStart="0dp"
android:background="#color/colorPrimary">
<com.fenchtose.flexiblemenu.FlexibleMenuContainer
android:id="#+id/menu_container2"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v7.widget.Toolbar>
</LinearLayout>
MainActivity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupMenu(R.id.menu_container, 6);
setupMenu(R.id.menu_container1, 2);
setupMenu(R.id.menu_container2, 4);
}
private void setupMenu(int id, int size) {
FlexibleMenuContainer container = (FlexibleMenuContainer) findViewById(id);
FlexibleMenu menu = new FlexibleMenu(populate(size), new FlexibleMenu.MenuCallback() {
#Override
public void onItemClicked(FlexibleMenu.MenuItem item) {
Toast.makeText(MainActivity.this, "menu selected: " + item.getId(), Toast.LENGTH_SHORT).show();
}
});
menu.inflate(container);
}
private List<FlexibleMenu.MenuItem> populate(int size) {
List<FlexibleMenu.MenuItem> items = new ArrayList<>();
for (int i=0; i<size; i++) {
View view = createView("Menu Item " + (i + 1));
items.add(new FlexibleMenu.MenuItem(i, view));
}
return items;
}
private TextView createView(String text) {
TextView view = new TextView(this);
view.setText(text);
view.setGravity(Gravity.CENTER);
view.setTextColor(0xffffffff);
return view;
}
}
Here is a solution that will left-justify the menu items while keeping the overflow menu icon to the right. This solution uses the standard implementation of the toolbar/action bar but anticipates how action views will be laid out so they will be positioned as we wish in the toolbar.
Most of the code below is what you have presented. I have moved the for loop that creates the menu items into onCreateOptionsMenu() so I could make use of the ActionMenuView that is already part of the toolbar's menu structure instead of adding another one.
In onCreateOptionsMenu() a running tally of space consumed by menu items is maintained as menu items are laid into the menu. As long as there is space, menu items will be flagged as "shown" (MenuItem.SHOW_AS_ACTION_ALWAYS). If the item will encroach on the area reserved for the overflow menu icon, the item is laid in but is targeted for the overflow menu (MenuItem.SHOW_AS_ACTION_NEVER).
After all views are laid into the menu, the slack space is computed. This is the area on the screen between the last visible menu item and the overflow icon (if overflow is in used) or between the last visible item and the end of the tool bar (if overflow is not in use.)
Once the slack space is computed, a Space widget is created and laid into the menu. This widget forces all other items to be left-justified.
Most of the changes have been made to MainActivity.java, but I may have changed a thing or two in the XML files. I include them here for completeness.
Here are some screen captures of the results.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Toolbar mToolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = findViewById(R.id.toolbar);
mToolbar.setTitle("");
setSupportActionBar(mToolbar); // Ensures that onCreateOptionsMenu is called
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
final float density = getResources().getDisplayMetrics().density;
final int overflowCellSize = (int) (OVERFLOW_CELL_WIDTH * density);
// Other than the overflow icon, this is how much real estate we have to fill.
int widthLeftToFill = mToolbar.getWidth() - overflowCellSize;
// slackWidth is what is left over after we are done adding our action views.
int slackWidth = -1;
for (int i = 0; i < 10; ++i) {
final View menuItemView =
LayoutInflater.from(this).inflate(R.layout.action_item, mToolbar, false);
ImageView imageView = menuItemView.findViewById(android.R.id.icon);
final int itemIconResId = R.drawable.ic_launcher_background;
imageView.setImageResource(itemIconResId);
final String text = "item" + i;
((TextView) menuItemView.findViewById(android.R.id.text1)).setText(text);
final View.OnClickListener onClickListener = new View.OnClickListener() {
#Override
public void onClick(final View view) {
Toast.makeText(MainActivity.this, text,
Toast.LENGTH_SHORT).show();
}
};
menuItemView.setOnClickListener(onClickListener);
final MenuItem menuItem = menu
.add(text).setActionView(menuItemView).setIcon(itemIconResId)
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
#SuppressLint("MissingPermission")
#Override
public boolean onMenuItemClick(final MenuItem menuItem) {
onClickListener.onClick(menuItemView);
return true;
}
});
// How wide is this ActionView?
menuItemView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
widthLeftToFill -= menuItemView.getMeasuredWidth();
if (widthLeftToFill >= 0) {
// The item will fit on the screen.
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
} else {
// The item will not fit. Force it to overflow.
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
if (slackWidth < 0) {
// Just crossed over the limit of space to fill - capture the slack space.
slackWidth = widthLeftToFill + menuItemView.getMeasuredWidth();
}
}
}
if (slackWidth < 0) {
// Didn't have enough action views to fill the width.
slackWidth = widthLeftToFill + overflowCellSize;
}
if (slackWidth > 0) {
// Create a space widget to consume the slack. This slack space widget makes sure
// that the action views are left-justified with the overflow on the right.
// As an alternative, this space could also be distributed among the action views.
Space space = new Space(this);
space.setMinimumWidth(slackWidth);
final MenuItem menuItem = menu.add("").setActionView(space);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
return true;
}
private static final int OVERFLOW_CELL_WIDTH = 40; // dips
}
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layoutDirection="ltr"
android:padding="0px"
android:theme="#style/ThemeOverlay.AppCompat.ActionBar"
app:contentInsetEnd="0px"
app:contentInsetEndWithActions="0px"
app:contentInsetLeft="0px"
app:contentInsetRight="0px"
app:contentInsetStart="0px"
app:contentInsetStartWithNavigation="0px"
app:logo="#null"
app:title="#null"
app:titleMargin="0px"
app:titleTextColor="#757575"
tools:ignore="UnusedAttribute"
tools:title="toolbar">
</android.support.v7.widget.Toolbar>
</FrameLayout>
action_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="false"
android:gravity="center"
android:orientation="horizontal"
android:paddingLeft="8dp">
<ImageView
android:id="#android:id/icon"
android:layout_width="wrap_content"
android:layout_height="?attr/actionBarSize"
android:scaleType="center"
tools:src="#android:drawable/sym_def_app_icon" />
<TextView
android:id="#android:id/text1"
android:layout_width="wrap_content"
android:layout_height="?attr/actionBarSize"
android:layout_marginLeft="6dp"
android:layout_marginStart="6dp"
android:gravity="center"
android:textColor="#c2555555"
android:textSize="15sp"
tools:text="text" />
</LinearLayout>
Update: To use the tool bar without setting it up as an action bar, add a global layout listener to wait until the tool bar is setup.
MainActivity.java - using a global layout listener instead of an action bar
public class MainActivity extends AppCompatActivity {
private Toolbar mToolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = findViewById(R.id.toolbar);
mToolbar.setTitle("");
// setSupportActionBar(mToolbar); // Ensures that onCreateOptionsMenu is called
mToolbar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mToolbar.getViewTreeObserver().removeOnGlobalLayoutListener(this);
setupMenu(mToolbar.getMenu());
}
});
}
public boolean setupMenu(Menu menu) {
final float density = getResources().getDisplayMetrics().density;
int mOverflowCellSize = (int) (OVERFLOW_CELL_WIDTH * density);
// Other than the overflow icon, this is how much real estate we have to fill.
int widthLeftToFill = mToolbar.getWidth() - mOverflowCellSize;
// slackWidth is what is left over after we are done adding our action views.
int slackWidth = -1;
for (int i = 0; i < 10; ++i) {
final View menuItemView =
LayoutInflater.from(this).inflate(R.layout.action_item, mToolbar, false);
ImageView imageView = menuItemView.findViewById(android.R.id.icon);
final int itemIconResId = R.drawable.ic_launcher_background;
imageView.setImageResource(itemIconResId);
String text = "item" + i;
((TextView) menuItemView.findViewById(android.R.id.text1)).setText(text);
final View.OnClickListener onClickListener = new View.OnClickListener() {
#Override
public void onClick(final View view) {
Toast.makeText(MainActivity.this, text ,
Toast.LENGTH_SHORT).show();
}
};
menuItemView.setOnClickListener(onClickListener);
final MenuItem menuItem = menu
.add(text).setActionView(menuItemView).setIcon(itemIconResId)
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
#SuppressLint("MissingPermission")
#Override
public boolean onMenuItemClick(final MenuItem menuItem) {
onClickListener.onClick(menuItemView);
return true;
}
});
// How wide is this ActionView?
menuItemView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
widthLeftToFill -= menuItemView.getMeasuredWidth();
if (widthLeftToFill >= 0) {
// The item will fit on the screen.
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
} else {
// The item will not fit. Force it to overflow.
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
if (slackWidth < 0) {
// Just crossed over the limit of space to fill - capture the slack space.
slackWidth = widthLeftToFill + menuItemView.getMeasuredWidth();
}
}
}
if (slackWidth < 0) {
// Didn't have enough action views to fill the width.
slackWidth = widthLeftToFill + mOverflowCellSize;
}
if (slackWidth > 0) {
// Create a space widget to consume the slack. This slack space widget makes sure
// that the action views are left-justified with the overflow on the right.
// As an alternative, this space could also be distributed among the action views.
Space space = new Space(this);
space.setMinimumWidth(slackWidth);
final MenuItem menuItem = menu.add("").setActionView(space);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
return true;
}
private static final int OVERFLOW_CELL_WIDTH = 40; // dips
}
The following sample app separates out menu creation from the left justification of the menu by introducing the method notifyMenuItemsChanged. In the app, click on the button to remove the menu item at position 1.
This code is basically the same as above, but the Space widget needs an id so it can be removed to be re-added when the menu changes.
MainActivity.Java: Sample app
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setTitle("");
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Menu menu = toolbar.getMenu();
// Remove item at position 1 on click of button.
if (menu.size() > 1) {
menu.removeItem(menu.getItem(1).getItemId());
notifyMenuItemsChanged(toolbar);
}
}
});
toolbar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
toolbar.getViewTreeObserver().removeOnGlobalLayoutListener(this);
setupMenu(toolbar);
}
});
}
private void setupMenu(Toolbar toolbar) {
Menu menu = toolbar.getMenu();
// Since we are resetting the menu, get rid of what may have been placed there before.
menu.clear();
for (int i = 0; i < 10; ++i) {
final View menuItemView =
LayoutInflater.from(this).inflate(R.layout.action_item, toolbar, false);
ImageView imageView = menuItemView.findViewById(android.R.id.icon);
final int itemIconResId = R.drawable.ic_launcher_background;
imageView.setImageResource(itemIconResId);
String text = "item" + i;
((TextView) menuItemView.findViewById(android.R.id.text1)).setText(text);
final View.OnClickListener onClickListener = new View.OnClickListener() {
#Override
public void onClick(final View view) {
Toast.makeText(MainActivity.this, text ,
Toast.LENGTH_SHORT).show();
}
};
menuItemView.setOnClickListener(onClickListener);
menu.add(Menu.NONE, View.generateViewId(), Menu.NONE, text)
.setActionView(menuItemView)
.setIcon(itemIconResId)
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
#SuppressLint("MissingPermission")
#Override
public boolean onMenuItemClick(final MenuItem menuItem) {
onClickListener.onClick(menuItemView);
return true;
}
});
}
// Now take the menu and left-justify it.
notifyMenuItemsChanged(toolbar);
}
/**
* Call this routine whenever the Toolbar menu changes. Take all action views and
* left-justify those that fit on the screen. Force to overflow those that don't.
*
* #param toolbar The Toolbar that holds the menu.
*/
private void notifyMenuItemsChanged(Toolbar toolbar) {
final int OVERFLOW_CELL_WIDTH = 40; // dips
final Menu menu = toolbar.getMenu();
final float density = getResources().getDisplayMetrics().density;
final int mOverflowCellSize = (int) (OVERFLOW_CELL_WIDTH * density);
// Other than the overflow icon, this is how much real estate we have to fill.
int widthLeftToFill = toolbar.getWidth() - mOverflowCellSize;
// slackWidth is what is left over after we are done adding our action views.
int slackWidth = -1;
MenuItem menuItem;
// Index of the spacer that will be removed/replaced.
int spaceIndex = View.NO_ID;
if (menu.size() == 0) {
return;
}
// Examine each MenuItemView to determine if it will fit on the screen. If it can,
// set its MenuItem to always show; otherwise, set the MenuItem to never show.
for (int i = 0; i < menu.size(); i++) {
menuItem = menu.getItem(i);
View menuItemView = menuItem.getActionView();
if (menuItemView instanceof Space) {
spaceIndex = menuItem.getItemId();
continue;
}
if (!menuItem.isVisible()) {
continue;
}
// How wide is this ActionView?
menuItemView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
widthLeftToFill -= menuItemView.getMeasuredWidth();
if (widthLeftToFill >= 0) {
// The item will fit on the screen.
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
} else {
// The item will not fit. Force it to overflow.
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
if (slackWidth < 0) {
// Just crossed over the limit of space to fill - capture the slack space.
slackWidth = widthLeftToFill + menuItemView.getMeasuredWidth();
}
}
}
if (spaceIndex != View.NO_ID) {
// Assume that this is our spacer. It may need to change size, so eliminate it for now.
menu.removeItem(spaceIndex);
}
if (slackWidth < 0) {
// Didn't have enough action views to fill the width, so there is no overflow.
slackWidth = widthLeftToFill + mOverflowCellSize;
}
if (slackWidth > 0) {
// Create a space widget to consume the slack. This slack space widget makes sure
// that the action views are left-justified with the overflow on the right.
// As an alternative, this space could also be distributed among the action views.
Space space = new Space(this);
space.setMinimumWidth(slackWidth);
// Need an if for the spacer so it can be deleted later if the menu is modified.
// Need API 17+ for generateViewId().
menuItem = menu.add(Menu.NONE, View.generateViewId(), Menu.NONE, "")
.setActionView(space);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
}
}
activity_main.xml: Sample app
<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"
android:orientation="vertical"
tools:context=".MainActivity">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Click the button to add/remove item #1 from the menu."/>
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Click to modify menu" />
</LinearLayout>

Moving inflated Layout/View to a new position in an animated way

I'm inflating a layout to show it on the screen.
Now I want to move that layout partially off screen. I tried that using .animate().translationX(-500) on the inflated layout. It moved off screen exactly how I wanted it to look:
Before:
After:
But now I have the problem that the area which the layout originally occupied is not clickable (e.g. can't swipe between homescreens, only works outside the blue marked area).
How can I solve that, so blue area is clickable after moving, but the remaing area of the layout (red area) could still register clicks (e.g. if I wanted to move it back in with an onClickListener)?
I think I have to work with updateViewLayout() function of the windowManager, but I don't know what I should change regarding the parameters.
Edit 1:
Here is the code. The translation is triggered by the button:
overlay.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#d8ff0000"
android:weightSum="1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="245dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Lorem ipsum "
android:id="#+id/textView6"
android:layout_marginLeft="10dp" />
<Button
android:layout_width="55dp"
android:layout_height="wrap_content"
android:text="X"
android:id="#+id/button"
android:layout_gravity="center_horizontal|top"
android:layout_alignParentTop="true"
android:layout_toEndOf="#+id/textView6"
android:layout_marginStart="27dp" />
</RelativeLayout>
</LinearLayout>
Service which inflates the layout:
OverlayService.java
public class FloatingMenu extends Service{
private WindowManager wm;
private LinearLayout ll;
private boolean isPushedToSide = true;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
ll = new LinearLayout(this);
LinearLayout.LayoutParams llParameters = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
ll.setBackgroundColor(Color.argb(50, 255, 255, 255));
ll.setLayoutParams(llParameters);
final WindowManager.LayoutParams parameters = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
parameters.gravity = Gravity.LEFT | Gravity.TOP;
LayoutInflater li = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
ll = (LinearLayout) li.inflate(R.layout.overlay, null);
final Button b = (Button) ll.findViewById(R.id.button);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isPushedToSide) {
ll.animate().translationX(0);
isPushedToSide = false;
}else {
ll.animate().translationX(-450);
isPushedToSide = true;
}
}
});
wm.addView(ll, parameters);
}
}
Edit 2:
While continuing my research I found out that .animate().translationX() only moves the position where the view is rendered and not the view itself. That also explains why after using .animate().translationX() the onclick event still gets triggered at the same position as before "moving" the layout.
Now I need to find a way to move the actual view to my desired position combining with an animation. Any ideas how to do that?
Edit 3:
I found a lot of posts with similar problems and if I'm right the solution to the problem is using ObjectAnimator instead of .animate().translationX().
I tried replacing that part in my code, so that the onClickListener now looks like this:
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isPushedToSide) {
//ll.animate().translationX(0);
ObjectAnimator.ofFloat(ll, "translationX", 0).setDuration(250).start();
isPushedToSide = false;
}else {
//ll.animate().translationX(-450);
ObjectAnimator.ofFloat(ll, "translationX", -450).setDuration(250).start();
isPushedToSide = true;
}
}
});
ll is the LinearLayout I have inflated, containing the Lorem ipsum text and Button.
Again the animation itself works fine, but behaves still the same way. I still need to click the original position of the button to fire the onClickListener and the blue area is still not clickable.
Edit 4:
Was trying out the suggestion to use.invalidate() to update the actual position. However it didn't work. I'm not sure if used it correct though.
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isPushedToSide) {
//ll.animate().translationX(0);
ObjectAnimator.ofFloat(ll, "translationX", 0).setDuration(250).start();
ll.invalidate();
isPushedToSide = false;
}else {
//ll.animate().translationX(-450);
ObjectAnimator.ofFloat(ll, "translationX", -450).setDuration(250).start();
ll.invalidate();
isPushedToSide = true;
}
}
});
I also tried changing the x value of LayoutParams and calling wm.updateViewLayou(ll, updatedParameters), which worked fine. When I moved the overlay 100px to the right, the area which triggers onClickListener also gets pushed 100px to the right. The problem is that I was only able to move the overlay within the boundaries of the actual screen and what I need is to move it off screen. Tied negative values, but that didn't work either.
What I essentially need is a drawer navigation but with a limited height (at least that's I think what I need). Gonna see if I can get something to work properly that way.
Edit 5:
Corrected my code so that .inflate() gets called when the animation has finished, but it still behaves the same way:
ObjectAnimator anim = ObjectAnimator.ofFloat(ll, "translationX", 0).setDuration(250);
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {}
#Override
public void onAnimationEnd(Animator animator) {
b.invalidate();
text.invalidate();
frame.invalidate();
ll.invalidate();
}
#Override
public void onAnimationCancel(Animator animator) {}
#Override
public void onAnimationRepeat(Animator animator) {}
});
anim.start();
I also recorded two .gifs showing the problem. I set the gravity to CENTER on the second one.
I think easy way to solve that problem is animate changing property "width" instead translate your layout.
hope it helps
I asked the creator of the guide I used for creating such SYSTEM_ALERT_WINDOW windows and he helped me out.
Here is the code:
public class Fw extends Service {
private WindowManager wm;
private boolean isPushedToSide = true;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
WindowManager.LayoutParams parameters;
Button b;
View inflating_layout;
LayoutInflater li;
ValueAnimator animator;
#Override
public void onCreate() {
super.onCreate();
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
li = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
inflating_layout = li.inflate(R.layout.overlay, null);
b = (Button) inflating_layout.findViewById(R.id.button);
parameters = new WindowManager.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSLUCENT);
parameters.gravity = Gravity.LEFT | Gravity.TOP;
isPushedToSide = true;
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isPushedToSide) {
isPushedToSide = false;
animator = ValueAnimator.ofInt(parameters.x, parameters.x-450);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
updateView(inflating_layout, val);
}
});
animator.setDuration(250);
animator.start();
} else {
animator = ValueAnimator.ofInt(parameters.x, parameters.x+450);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
updateView(inflating_layout, val);
}
});
animator.setDuration(250);
animator.start();
isPushedToSide = true;
}
}
});
wm.addView(inflating_layout, parameters);
}
public void updateView(View view, Integer x) {
if (view != null) {
if (x != null) parameters.x = x;
wm.updateViewLayout(view, parameters);
}
}
}
Now everything seems to work fine. Only the sliding animation is flickering a bit at the border of the screen. With a slower speed e.g. .setDuration(2500) that doesn't happen. I don't know if it's because of the emulator or another reason.

Android : Locating the item at center position of the screen after selecting it ,In the horizontalscrollview

I am trying to scroll item to the middle position of the screen after selecting it.I have used Horizontalscrollview and LinearLayout inside it to add item. I show in the XML
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"
android:orientation="vertical"
tools:context=".MainActivity" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/bottom"
android:orientation="vertical" >
<HorizontalScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/updown"
android:layout_weight="0.8"
android:fadingEdgeLength="0dp" >
<LinearLayout
android:id="#+id/horizontalbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/tlbackground"
android:baselineAligned="true"
android:orientation="horizontal" >
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
</RelativeLayout>
Class
public class MainActivity extends FragmentActivity implements ActionBar.TabListener
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
hsl = (HorizontalScrollView) findViewById(R.id.scrollView);
LinearLayout l1 = (LinearLayout)findViewById(R.id.horizontalbar);
for(int i = 0; i < 20; i++)
{
Button b = new Button(this);
b.setText("t"+i);
ll.addView(b);
}
}
}
I want something like in figure
before click
after click
How can I Scroll that selected item to the middle in HorizontalScrollview?
After long try , I have got it with this
for (int i = 0; i < 20; i++) {
final Button b = new Button(this);
b.setText("t" + i);
b.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
int scrollX = (b.getLeft() - (screenWidth / 2)) + (b.getWidth() / 2);
hsl.smoothScrollTo(scrollX, 0);
}
});
ll.addView(b);
}
This is how your onCreate should look like:
hsl = (HorizontalScrollView) findViewById(R.id.scrollView);
LinearLayout ll = (LinearLayout) findViewById(R.id.horizontalbar);
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
final int width = size.x;
for (int i = 0; i < 20; i++) {
final Button b = new Button(this);
b.setText("t" + i);
b.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
int center = (width - b.getWidth())/2;
hsl.scrollTo(b.getLeft() - center, b.getTop());
}
});
ll.addView(b);
}
The only thing i didn't discover is why i need to substract additional 200px (in my case). Probably some padding or something else. Instead of 200, you could discover how many dips is it and call function:
public static int convertDipToPixels(Context c, float dips) {
return (int) (dips * c.getResources().getDisplayMetrics().density + 0.5f);
}

Creating Multiple ImageButtons in a Single Activity Programatically

I have a single image myimage.png which is placed in my res-drawable folder.
I have to create 50 ImageButtons in an activity all using this same image.
And then when a user clicks i need to pop out a toast saying button number i was clicked.
Here's what I have done:
public class AllImageButtons extends Activity {
int screendimesionx;
int screendimesiony;
ImageButton imageButton;
ImageButton allImageButtons[] = new ImageButton[50];
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.allbuttonimages);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
screendimesiony = metrics.heightPixels;
screendimesionx = metrics.widthPixels;
createButtonsAndAddListener();
}
public void createButtonsAndAddListener() {
for (int i = 0; i < 50; i++) {
imageButton = (ImageButton) findViewById(R.id.myimage);
float buttonimagey = imageButton.getDrawable().getBounds().height();
float buttonimagex = imageButton.getDrawable().getBounds().width();
float xspaceforeachbuttonimage = screendimesionx/50;
LayoutParams par = (LayoutParams)imageButton.getLayoutParams();
par.leftMargin = (int) (i*xspaceforeachbuttonimage);
par.topMargin = 0;
imageButton.setLayoutParams(par);
allImageButtons[i] = imageButton;
allImageButtons[i].setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Toast.makeText(AllbuttonimagesForSelectionActivity.this,
"ImageButton is clicked!", Toast.LENGTH_SHORT)
.show();
}
});
}
}}
and then there is the associated xml file allbuttonimages.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ImageButton
android:id="#+id/imageButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/myimage" />
</LinearLayout>
This xml layout displays only the last ImageButton from the loop.
My Question:
How can i dynamically create all 50 ImageButtons from the same image in a single view ?
You are not adding the buttons to the parent .
use addView(yourImageButton) to add your image button to activity
The button that is visible now is the one in xml
You should create new imageButton using new
eg: ImageButton imgBtn = new ImageButton(this)
Then set the properties and addView to your parent Layout
You will not get 50 buttons with the one defined in xml
You have one button in layout and you are calling findViewById for that.
You need to create new buttons each time and add it to the linearlayout.
public void createButtonsAndAddListener() {
for (int i = 0; i < 50; i++) {
imageButton = new ImageButton(this);
imageButton.setImageResource(R.drawable.myimage);
float buttonimagey = imageButton.getDrawable().getBounds().height();
float buttonimagex = imageButton.getDrawable().getBounds().width();
float xspaceforeachbuttonimage = screendimesionx/50;
LayoutParams par = (LayoutParams)imageButton.getLayoutParams();
par.leftMargin = (int) (i*xspaceforeachbuttonimage);
par.topMargin = 0;
imageButton.setLayoutParams(par);
allImageButtons[i] = imageButton;
allImageButtons[i].setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Toast.makeText(AllbuttonimagesForSelectionActivity.this,
"ImageButton is clicked!", Toast.LENGTH_SHORT)
.show();
}
});
((LinearLayout)findViewById(R.id.ll)).addView(imageButton);
}
Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" android:id="#+id/ll">
</LinearLayout>
Your code wont add images to the layout. Get the parent layout in code and add images to it using addView. Between you do not need the image button in the xml. You can do some thing like this:
LinearLayout parent = (LinearLayout)findViewById(R.id.the_parent_layout);
for(int i = 0; i< 50; i++){
ImageButton image = new ImageButton(context);
//set whatever properties you want
//then add to the parent
parent.addView(image); // you can also specify the layout params here
}
Try This....
for (int i = 0; i < 50; i++) {
ImageButton b1 = new ImageButton(myrefmenu);
b1.setId(100 + i);
b1.setImageResource(R.drawable.imagename);
// b1.setText(adapt_objmenu.city_name_array[i]);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if (i > 0) {
lp.addRule(RelativeLayout.RIGHT_OF, b1.getId() - 1);
}
b1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Toast.makeText(AllbuttonimagesForSelectionActivity.this,
"ImageButton is clicked!", Toast.LENGTH_SHORT)
.show();
}
});
b1.setLayoutParams(lp);
relative.addView(b1);
}

Android: first element of a GridView is misplaced in galaxy S3 and S4

I'm building an app that has a mini slotMachine game inside. The problem is that I use a GridView for each column of the slot and for some devices (not all) the first symbol of the slot has a space above that comes from nowhere...
The table of the slot is 3 rows X 5 columns.
The space doesn't come from the calculation of the symbol width and height because I've found that with two devices withe perfectly identical resolution and density (Galaxy tab 10.1 and Galaxy note 10.1) the spacing is different: 0 for one and 5 for the other.
Any help?
My MainActivity:
public class MainActivity extends Activity{
SlotView slot;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
slot=new SlotView(this);
slot.refreshSlotView("AAAAAAAAAAAAAAA");
}
}
SlotView:
public class SlotView {
private ArrayList<GridAdapter> adapter=new ArrayList<GridAdapter>();
public float H_RATIO=2/3; //height ratio of the grid based on the device
public float W_RATIO=5/6; //width ratio of the grid based on the device
Activity activity;
String path = "file:///android_asset/Slot/Slot-";
public SlotView(Context c){
activity = (Activity) c;
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
final int hDisplay=dm.heightPixels;
int wImage=(1024*hDisplay)/768; //width of the central image
float heightGrid=(hDisplay*2)/3; //grid height
float widthGrid=((wImage*5)/6); //grid width
float heightSymbol=(heightGrid-(8))/3; // symbol height (3 symbols for column with a spacing of 4 px)
//relative layout for the container table
RelativeLayout.LayoutParams params=new RelativeLayout.LayoutParams((int)widthGrid, (int)heightGrid);
params.addRule(RelativeLayout.CENTER_IN_PARENT);
// grid of the slot
LinearLayout grid_layout=(LinearLayout)activity.findViewById(R.id.layout_grid);
grid_layout.setLayoutParams(params);
grid_layout.setGravity(Gravity.TOP);
// gridview of the columns
LinearLayout.LayoutParams params_grid=new LinearLayout.LayoutParams((int)heightSymbol, (int)heightGrid);
for(int i=0; i<5; i++){ // five columns
GridView slot=new GridView(activity);
slot.setScrollContainer(false);
slot.setVerticalScrollBarEnabled(false);
slot.setHorizontalScrollBarEnabled(false);
if(i<4) {
params_grid.setMargins(0,0,4,0); // spacing between each column
}
slot.setLayoutParams(params_grid);
slot.setVerticalSpacing(4);
slot.setPadding(0, 0, 0, 0);
slot.setColumnWidth((int)heightSymbol);
slot.setNumColumns(GridView.AUTO_FIT);
slot.setGravity(Gravity.CENTER);
GridAdapter grid_adapter=new GridAdapter(activity, (int)heightSymbol, (int)heightSymbol);
adapter.add(grid_adapter);
slot.setAdapter(grid_adapter);
grid_layout.addView(slot);
}
}
public void refreshSlotView(String configTris){
for(int pos=0; pos<5; pos++){
String[] mThumbIds=new String[3];
int z=0;
for(int i=pos; z<3 && i<configTris.length(); i=i+5){
char letter=configTris.charAt(i);
mThumbIds[z]=path + letter + ".png";
z++;
}
adapter.get(pos).setArrayOfImage(mThumbIds);
adapter.get(pos).notifyDataSetChanged();
}
}
}
The adapter:
public class GridAdapter extends BaseAdapter {
private Context mContext;
private String[] mThumbIds={};
private int w;
private int h;
public GridAdapter(Context c, int w, int h) {
mContext = c;
this.w=w;
this.h=h;
}
public int getCount() {
return mThumbIds.length;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
View view=convertView;
ImageView imageView;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.slot_element, null);
RelativeLayout rel=(RelativeLayout)view.findViewById(R.id.rel);
rel.setLayoutParams(new GridView.LayoutParams(w, h));
imageView=(ImageView)view.findViewById(R.id.image);
RelativeLayout.LayoutParams params=new RelativeLayout.LayoutParams(w, h);
params.addRule(RelativeLayout.CENTER_IN_PARENT);
imageView.setLayoutParams(params);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
}else{
imageView = (ImageView) view.findViewById(R.id.image);
}
Picasso.with(mContext).load(mThumbIds[position]).into(imageView); // puts the image in the imageview
return view;
}
public void setArrayOfImage(String[] images){
this.mThumbIds=images;
}
public void showSymbolAtIndex(char letter, int position){
mThumbIds[position]="file:///android_asset/" + "Slot/Slot-" + letter + ".png";
notifyDataSetChanged();
}
}
This is the MainActivity Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/layout_grid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="horizontal" >
</LinearLayout>
</RelativeLayout>
And the slot_element Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rel"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY" />
</RelativeLayout>
Now that's the result:
And that's the proof of the existing spacing (if I drag one of the columns up):

Categories

Resources