I'm implementing a container with 3 buttons, added dynamically. When I select one, I want that the other ones are deselected (change drawable). This works when I add the buttons directly to a LinearLayout, which I declared in XML. But when I create a custom layout, which extends LinearLayout, and put my buttons there, it doesn't work anymore (although the code is almost the same). I select one button and the code to deselect the other ones is executed, but they still show the drawable of selected state.
Edit: I always can select the buttons, in both cases. Selection works. What doesn't work in the custom view case is de -selecting the views, or at least the drawable is not changed when the views are deselected.
I reduced the code to the minimal necessary to see the problem.
The activity:
public class TestActivity extends Activity {
private View[] views;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
/////////////////////////////////////////////////////////////////
//uncomment this block to see the working code (without custom layout)
// views = new View[3];
// LinearLayout root = (LinearLayout) findViewById(R.id.container);
// findViewById(R.id.myCustomLayout).setVisibility(View.GONE);
// addButton(root, 0);
// addButton(root, 1);
// addButton(root, 2);
//////////////////////////////////////////////////////////////////
}
private void addButton(final ViewGroup root, final int position) {
View v = new View(this);
LinearLayout.LayoutParams viewLayoutPars = new LinearLayout.LayoutParams(150, 200);
v.setLayoutParams(viewLayoutPars);
v.setBackgroundResource(R.drawable.mybg);
if (position == 0) {
v.setSelected(true);
}
root.addView(v);
views[position] = v;
v.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
for (View view : views) {
view.setSelected(false);
}
v.setSelected(true);
root.invalidate();
}
});
}
}
The layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.test.MyCustomLayout
android:id="#+id/myCustomLayout"
xmlns:dg="http://schemas.android.com/apk/res/com.test"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
My custom layout:
public class MyCustomLayout extends LinearLayout {
private Context context;
private View[] views;
public MyCustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
views = new View[3];
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
addButton(0);
addButton(1);
addButton(2);
}
private void addButton(final int position) {
View v = new View(context);
LinearLayout.LayoutParams viewLayoutPars = new LinearLayout.LayoutParams(150, 200);
v.setLayoutParams(viewLayoutPars);
v.setBackgroundResource(R.drawable.mybg);
if (position == 0) {
v.setSelected(true);
}
addView(v);
views[position] = v;
v.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
for (View view : views) {
view.setSelected(false);
}
v.setSelected(true);
invalidate();
}
});
}
}
The drawable (mybg):
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="#drawable/selected" />
<item android:drawable="#drawable/not_selected" />
</selector>
The images are 2 harmless squares selected.png and not_selected.png with different color.
Thanks in advance.
Related
What I want is that when the user clicks a list item in a ListView, it converts to a whole activity (as you can see in the following example), but I was not able to find a tutorial explaining this and, actually, I do not know how this movement is called.
In other words, what I want to achieve is:
Increase List Item elevation when it is clicked (as you can see in the right gif)
Expand and transform list item to the next fragment/activity layout that contains detailed information about the clicked item
I have tried a lot of transitions but with no luck. Can anyone help me out to accomplish this?
I build a small sample application that transitions between two activities with the desired effect:
However the transitions in the provided gifs are slightly different. The
transition in the gif on the left side transitions the list element into the content area of the second activity (Toolbar stays in place). In the gif on the right side the transition transforms the list element into the complete screen of the second activity. The following code provides the effect in the left gif. However it should be possible to adapt the solution with minor modifications to achieve the transition in the right gif.
Note this only works on Lollipop. However it is possible to mock a different effect on older devices.
Furthermore the sole purpose of the provided code is to show how it could be done. Don't use this directly in your app.
MainActivity:
public class MainActivity extends AppCompatActivity {
MyAdapter myAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
ListView listView = (ListView) findViewById(R.id.list_view);
myAdapter = new MyAdapter(this, 0, DataSet.get());
listView.setAdapter(myAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, final View view, final int position, long id) {
startTransition(view, myAdapter.getItem(position));
}
});
}
private void startTransition(View view, Element element) {
Intent i = new Intent(MainActivity.this, DetailActivity.class);
i.putExtra("ITEM_ID", element.getId());
Pair<View, String>[] transitionPairs = new Pair[4];
transitionPairs[0] = Pair.create(findViewById(R.id.toolbar), "toolbar"); // Transition the Toolbar
transitionPairs[1] = Pair.create(view, "content_area"); // Transition the content_area (This will be the content area on the detail screen)
// We also want to transition the status and navigation bar barckground. Otherwise they will flicker
transitionPairs[2] = Pair.create(findViewById(android.R.id.statusBarBackground), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME);
transitionPairs[3] = Pair.create(findViewById(android.R.id.navigationBarBackground), Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME);
Bundle b = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, transitionPairs).toBundle();
ActivityCompat.startActivity(MainActivity.this, i, b);
}
}
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/colorPrimary"
android:transitionName="toolbar" />
<ListView
android:id="#+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
DetailActivity:
public class DetailActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
long elementId = getIntent().getLongExtra("ITEM_ID", -1);
Element element = DataSet.find(elementId);
((TextView) findViewById(R.id.title)).setText(element.getTitle());
((TextView) findViewById(R.id.description)).setText(element.getDescription());
// if we transition the status and navigation bar we have to wait till everything is available
TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar(this);
// set a custom shared element enter transition
TransitionHelper.setSharedElementEnterTransition(this, R.transition.detail_activity_shared_element_enter_transition);
}
}
activity_detail.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/colorPrimary"
android:transitionName="toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#abc"
android:orientation="vertical"
android:paddingBottom="200dp"
android:transitionName="content_area"
android:elevation="10dp">
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
detail_activity_shared_element_enter_transition.xml (/res/transition/):
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:transitionOrdering="together">
<changeBounds/>
<changeTransform/>
<changeClipBounds/>
<changeImageTransform/>
<transition class="my.application.transitions.ElevationTransition"/>
</transitionSet>
my.application.transitions.ElevationTransition:
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class ElevationTransition extends Transition {
private static final String PROPNAME_ELEVATION = "my.elevation:transition:elevation";
public ElevationTransition() {
}
public ElevationTransition(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void captureStartValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
#Override
public void captureEndValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
private void captureValues(TransitionValues transitionValues) {
Float elevation = transitionValues.view.getElevation();
transitionValues.values.put(PROPNAME_ELEVATION, elevation);
}
#Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
Float startVal = (Float) startValues.values.get(PROPNAME_ELEVATION);
Float endVal = (Float) endValues.values.get(PROPNAME_ELEVATION);
if (startVal == null || endVal == null || startVal.floatValue() == endVal.floatValue()) {
return null;
}
final View view = endValues.view;
ValueAnimator a = ValueAnimator.ofFloat(startVal, endVal);
a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
view.setElevation((float)animation.getAnimatedValue());
}
});
return a;
}
}
TransitionHelper:
public class TransitionHelper {
public static void fixSharedElementTransitionForStatusAndNavigationBar(final Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return;
final View decor = activity.getWindow().getDecorView();
if (decor == null)
return;
activity.postponeEnterTransition();
decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
public boolean onPreDraw() {
decor.getViewTreeObserver().removeOnPreDrawListener(this);
activity.startPostponedEnterTransition();
return true;
}
});
}
public static void setSharedElementEnterTransition(final Activity activity, int transition) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return;
activity.getWindow().setSharedElementEnterTransition(TransitionInflater.from(activity).inflateTransition(transition));
}
}
So what are the different parts here:
We have two activities. During the transition four views are transitioned between the activities.
Toolbar: like in the left gif the toolbar doesn't move with the rest of the content.
ListView element View -> becomes the content view of the DetailActivity
StatusBar and NavigationBar Background: If we don't add these views to the set of transitioned views they will fade out and back in during the transition. This however requires to delay the enter transition (see: TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar)
In the MainActivity the transitioned views are added to the Bundle that is used to start the DetailActivity. Furthermore the transitioned views need to be named (transitionName) in both activities. This can be done in the layout xml as well as programatically.
The default set of transitions, that is used during the shared element transition, affects different aspects of the view(for example: view bounds - see 2). However differences in the elevation of a view are not animated. This is why the presented solution utilizes the custom ElevationTransition.
try this.. Material-Animations
blueIconImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, SharedElementActivity.class);
View sharedView = blueIconImageView;
String transitionName = getString(R.string.blue_name);
ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName);
startActivity(i, transitionActivityOptions.toBundle());
}
});
The Animation you need is called Activity Transitions between shared elements.
By Research I found that you should:
Put your ListView view in a relativeLayout
OnClick, inflate a copy of your renderer
Find the global coordinates for where the renderer sits in
relationship to the parent of the ListView
Add the copied renderer to the RelativeLayout (parent of ListView)
Animate the listView away
On the end of that animate, animate your new renderer
Profit!
public class MainActivity extends Activity {
private RelativeLayout layout;
private ListView listView;
private MyRenderer selectedRenderer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
layout = new RelativeLayout(this);
setContentView(layout);
listView = new ListView(this);
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
layout.addView(listView, rlp);
listView.setAdapter(new MyAdapter());
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// find out where the clicked view sits in relationship to the
// parent container
int t = view.getTop() + listView.getTop();
int l = view.getLeft() + listView.getLeft();
// create a copy of the listview and add it to the parent
// container
// at the same location it was in the listview
selectedRenderer = new MyRenderer(view.getContext());
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(view.getWidth(), view
.getHeight());
rlp.topMargin = t;
rlp.leftMargin = l;
selectedRenderer.textView.setText(((MyRenderer) view).textView.getText());
layout.addView(selectedRenderer, rlp);
view.setVisibility(View.INVISIBLE);
// animate out the listView
Animation outAni = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, -1f, Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, 0f);
outAni.setDuration(1000);
outAni.setFillAfter(true);
outAni.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
ScaleAnimation scaleAni = new ScaleAnimation(1f,
1f, 1f, 2f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scaleAni.setDuration(400);
scaleAni.setFillAfter(true);
selectedRenderer.startAnimation(scaleAni);
}
});
listView.startAnimation(outAni);
}
});
}
public class MyAdapter extends BaseAdapter {
#Override
public int getCount() {
return 10;
}
#Override
public String getItem(int position) {
return "Hello World " + position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
MyRenderer renderer;
if (convertView != null)
renderer = (MyRenderer) convertView;
else
renderer = new MyRenderer(MainActivity.this);
renderer.textView.setText(getItem(position));
return renderer;
}
}
public class MyRenderer extends RelativeLayout {
public TextView textView;
public MyRenderer(Context context) {
super(context);
setPadding(20, 20, 20, 20);
setBackgroundColor(0xFFFF0000);
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
rlp.addRule(CENTER_IN_PARENT);
textView = new TextView(context);
addView(textView, rlp);
}
} }
Try this spectacular webpage # Getting Started with Activity & Fragment Transitions (part 1). Here they talked about Activity and Fragment Transitions. I have not tried it. My view is that Fragment Transitions is better and less computer intensive, so it's a good start. And you may not need to change Toolbars, you can show/hide them.
Another good SO link is # Animate the transition between fragments, look at the best answer. In that post, they talked about objectAnimator.
Another opinion is about the sample animation you posted, it does not show a smooth animation from one art to another. It is less impressive when the animation is not smooth.
Good luck, have fun, keep us all posted.
I've created a custom RelativeLayout view which I inflate with merge tags.
In this custom view I have a button which I want it to do something.
I've tried many things but the button simply refuses to be clicked.
The strange part is, I can find the views and change their visibility just fine.
Is it possible to click a button this way, or should it be done in a different way?
The things I've tried:
Anonymous innerclass for onClickListener
XML attribute for onClick
View onClick with onClickListener(this)
Check if it's clickable with code (it returns true)
Added clickable(true) to both XML and code.
Private context from constructor instead of getContext()
Moved logic from init() to onFinishInflate()
Used the view from inflater to find views
Switch from inflate() to LayoutInflater
Inflated custom view:
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/internet_view_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="#string/internet_offline"/>
<custom.IconView
android:id="#+id/internet_view_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="ICON"
android:visibility="invisible"
android:layout_below="#+id/internet_view_text"/>
<Button
android:id="#+id/internet_view_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="BUTTON"
android:clickable="true"
android:layout_below="#+id/internet_view_text"/>
</merge>
Relevant part of the parent view:
<custom.NoInternetView
android:id="#+id/webview_no_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"
app:automaticReconnect="false"/>
The class file:
public class NoInternetView extends RelativeLayout implements View.OnClickListener {
private static final String TAG = NoInternetView.class.getSimpleName();
private static ConnectivityChangeListener listener;
private static boolean automaticReconnect;
private Context mContext;
private View view;
private Button button;
public NoInternetView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NoInternetView, defStyleAttr, 0);
automaticReconnect = a.getBoolean(R.styleable.NoInternetView_automaticReconnect, false);
a.recycle();
init();
}
public NoInternetView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NoInternetView(Context context) {
this(context, null);
}
#Override
protected void onFinishInflate() {
Log.d(TAG, "onFinishInflate");
super.onFinishInflate();
button = (Button) view.findViewById(R.id.internet_view_button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Clicked on some Button");
if (listener != null && NetworkHelper.hasAccess(getContext())) {
listener.connected();
}
}
});
button.setClickable(true);
boolean clicked = button.callOnClick();
Log.d(TAG, "clicked: "+clicked);
TextView text = (TextView) view.findViewById(R.id.internet_view_icon);
text.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Clicked on some TextView");
if (listener != null && NetworkHelper.hasAccess(getContext())) {
listener.connected();
}
}
});
//check if the attribute 'automaticReconnect' is set to true
//if so, show an icon instead of a button
if (automaticReconnect){
button.setVisibility(INVISIBLE);
view.findViewById(R.id.internet_view_icon).setVisibility(VISIBLE);
}
}
public void init(){
Log.d(TAG, "init");
view = LayoutInflater.from(mContext).inflate(R.layout.no_internet_view, this, false);
}
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
Log.d(TAG, "something happened?");
return super.dispatchKeyEvent(event);
}
#Override
public void onClick(View v) {
Log.d(TAG, "Clicked on Button(TextView)");
if (listener != null && NetworkHelper.hasAccess(getContext())){
listener.connected();
}
}
So I got it to work. Here are the things I did to make it work.
Replace the merge tag with RelativeLayout, because you are inflating the layout to show inside a view. More info here: Android xml merge layout error on inflate
You have set android:visibility="invisible", change that to android:visibility="visible" (This is just to get the view visible, you can later change it programatically)
Add the below line to the init() method of your NoInternetView. You are inflating the layout with all the views, but only when you add this view, you will be able to see it.
public void init(){
Log.d(TAG, "init");
view = LayoutInflater.from(mContext).inflate(R.layout.no_internet_view, this, false);
this.addView(view);}
Once you are done with this, the button will be visible and clicking on the button will be clickable. You can either use anonymous onClickListener or make the view implement the onClickListener and override the onClick() method. Anything works.
I've got an android app, with a super-class that contains a layout that should be static for each activity, the illustration beneath shows this in a better way rather than my description
This "header" contains a tabBar which contains a ImageButton. I want this header to be static for all the activities in my app. What I tried to do, is to extend my other classes from this superclass. Code for the super class is beneath
public class MySuperClass extends Activity {
MyHorizontalScrollView scrollView;
View menu;
View app;
ImageButton btnSlide;
boolean menuOut = false;
Handler handler = new Handler();
int btnWidth;
Button testClass;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflater = LayoutInflater.from(this);
scrollView = (MyHorizontalScrollView) inflater.inflate(R.layout.horz_scroll_with_list_menu, null);
setContentView(scrollView);
menu = inflater.inflate(R.layout.horz_scroll_menu, null);
app = inflater.inflate(R.layout.horz_scroll_app, null);
ViewGroup tabBar = (ViewGroup) app.findViewById(R.id.tabBar);
ListView listView = (ListView) app.findViewById(R.id.list);
listView = (ListView) menu.findViewById(R.id.list);
ArrayList<MenuItem> menuItems = getMenuItems();
listView.setAdapter(new MenuCustomAdapter(this, menuItems));
btnSlide = (ImageButton) tabBar.findViewById(R.id.BtnSlide);
btnSlide.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
btnSlide.setImageResource(R.drawable.lincolor);
break;
case MotionEvent.ACTION_UP:
btnSlide.setImageResource(R.drawable.lin);
break;
}
return false;
}
});
btnSlide.setOnClickListener(new ClickListenerForScrolling(scrollView, menu));
testClass = (Button) app.findViewById(R.id.button1);
testClass.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Intent intent = new Intent(MySuperClass.this, TestClass.class);
startActivity(intent);
}
});
final View[] children = new View[] { menu, app };
// Scroll to app (view[1]) when layout finished.
int scrollToViewIdx = 1;
scrollView.initViews(children, scrollToViewIdx, new SizeCallbackForMenu(btnSlide));
}
public ArrayList<MenuItem> getMenuItems() {
ArrayList<MenuItem> items = new ArrayList<MenuItem>();
MenuItem m1 = new MenuItem(R.drawable.scroll, "Show history");
items.add(m1);
MenuItem m2 = new MenuItem(R.drawable.right, "Right");
items.add(m2);
return items;
}
/**
* Helper for examples with a HSV that should be scrolled by a menu View's width.
*/
static class ClickListenerForScrolling implements OnClickListener {
HorizontalScrollView scrollView;
View menu;
ImageButton button;
int pressed;
int timeout;
/**
* Menu must NOT be out/shown to start with.
*/
boolean menuOut = false;
public ClickListenerForScrolling(HorizontalScrollView scrollView, View menu) {
super();
this.scrollView = scrollView;
this.menu = menu;
}
#Override
public void onClick(View v) {
Context context = menu.getContext();
int menuWidth = menu.getMeasuredWidth();
// Ensure menu is visible
menu.setVisibility(View.VISIBLE);
if (!menuOut) {
// Scroll to 0 to reveal menu
int left = 0;
scrollView.smoothScrollTo(left, 0);
} else {
// Scroll to menuWidth so menu isn't on screen.
int left = menuWidth;
scrollView.smoothScrollTo(left, 0);
}
menuOut = !menuOut;
}
}
/**
* Helper that remembers the width of the 'slide' button, so that the 'slide' button remains in view, even when the menu is
* showing.
*/
static class SizeCallbackForMenu implements SizeCallback {
int btnWidth;
View btnSlide;
public SizeCallbackForMenu(View btnSlide) {
super();
this.btnSlide = btnSlide;
}
#Override
public void onGlobalLayout() {
btnWidth = btnSlide.getMeasuredWidth();
System.out.println("btnWidth=" + btnWidth);
}
#Override
public void getViewSize(int idx, int w, int h, int[] dims) {
dims[0] = w;
dims[1] = h;
final int menuIdx = 0;
if (idx == menuIdx) {
dims[0] = w - btnWidth;
}
}
}
}
And a test class, which extends this superclass.
public class TestClass extends MySuperClass {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
}
}
Again how can I make the tabBar static for each activity?
There is no way to achieve this in an Activity-scope. The layout are independent from your Acitvity (well, until you bind them).
The solution to your problem, may be in using a common header layout, kept in a xml file under your layout folder, something like this:
header.xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/red"
... />
And include them in your layouts, using the include tag:
my_activity_layout.xml:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
....
.... >
<include layout="#layout/header.xml" android:id="#+id/header" />
<!-- Countinue your layout .... -->
</RelativeLayout>
you can use a TabHost right. by click on each tab you can launch separate activity
http://mfarhan133.wordpress.com/2010/11/17/tablayouttabhost-tutorial-for-android-reusing-layout/
I don't know if that is possible. I will post an alternative solution, which requires a little bit of more code but works, and is a best practice, so if nothing comes up this is the best alternative, probably the only one.
When you are trying to use a certain layout again and again, like the tab bar that you are mentioning, there is the <merge> and <include> functionality in the layout. The basic idea is that you make a layout.xml file that you want to include in other layouts.
This post gives a very good example of how to use it. Simple example of <merge> and <include> usage in Android XML-layouts
I have list with items having two textview and one imageview.I inflate the list with ArrayAdapter.Everything is working fine except changing list item color on click.I have 22 items in my listview. Primarily listview displaying 10 items on the screen and get the other items on scroll.Now my problem is when I clicks on a single item between 0-9(initial 10 items) item changes their color properly on click, but when i scroll and clicks on an item having position greater then 9(after the initial 10 items) my activity crashes.I am referring http://www.mail-archive.com/android-developers#googlegroups.com/msg09740.html link to write the code in for loop.Help me to get rid of this problem.Any suggestions or solutions will be highly appreciated.Thanx in advance.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); // to hide the virtual keyboard
setContentView(R.layout.defect_pic_listview);
try{
adapter = new MyArrayAdapter(this,makeList());
setListAdapter(adapter);
adapter.notifyDataSetChanged();
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
getListView().setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// Toast.makeText(getApplicationContext(), "Item "+position+" is clicked",
// Toast.LENGTH_SHORT).show();
System.out.println("position"+position);
int first = getListView().getFirstVisiblePosition();
System.out.println("first="+first);
int last = getListView().getLastVisiblePosition();
System.out.println("last="+last);
int total = last - first;
System.out.println("total="+total);
if(getListView().isItemChecked(position)){
for(int i = 0 ; i <= last ; i++){
System.out.println("i="+i);
if(first+i == position){
getListView().getChildAt(i).setBackgroundColor(Color.GREEN);
System.out.println("l1="+getListView());
// l.getItemAtPosition(i);
// System.out.println("l position"+l);
}
else{
getListView().getChildAt(i).setBackgroundColor(Color.TRANSPARENT);
System.out.println("l2="+getListView());
}
}
}
else{
getListView().getChildAt(position - first).setBackgroundColor(Color.TRANSPARENT);
}
}
});
}
catch(Exception e){
Log.d("error",e.getMessage());
}
}
Use this code for the for loop.
if(getListView().isItemChecked(position))
{
for(int i = 0 ; i < total ; i++)
{
System.out.println("i="+i);
if(first+i == position)
{
getListView().getChildAt(i).setBackgroundColor(Color.GREEN);
System.out.println("l1="+getListView());
// l.getItemAtPosition(i);
// System.out.println("l position"+l);
}
else
{
getListView().getChildAt(i).setBackgroundColor(Color.TRANSPARENT);
System.out.println("l2="+getListView
());
}
}
I think your intention to only change the bacground color of clicked item. For that you not need to add for loop. if your intention is same as i said then use this code only at the place of for loop.
getListView().getChildAt(position).setBackgroundColor(Color.GREEN);
There is other way to do.
First you need to add a selector xml e.g. listviewitem_bg.xml
<item android:drawable="#drawable/listview_normal" android:state_enabled="true" android:state_pressed="false"/>
<item android:drawable="#drawable/listview_press" android:state_enabled="true" android:state_pressed="true"/>
and then set it as a background of your list view cell.
You can try like this:
public class CustomAdapter extends ArrayAdapter<String> {
String[] array;
Context mContext;
LayoutInflater mInflater;
int[] itemStates;
public CustomAdapter(Context context, int textViewResourceId,
String[] objects)
{
super(context, textViewResourceId, objects);
array=objects;
mContext=context;
mInflater = LayoutInflater.from(context);
//save all buttons state as 0(not clicked) initially
itemStates=new int[objects.length];
for(int i=0;i<objects.length;i++)
{
itemStates[i]=0;
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
final ViewHolder holder;
if(convertView==null)
{
convertView = mInflater.inflate(R.layout.custom_listitem, null);
holder = new ViewHolder();
holder.text=(TextView)convertView.findViewById(R.id.text);
holder.layout=(LinearLayout)convertView.findViewById(R.id.linear_layout); // outer most linear layout iin custom_listitem xml
convertView.setTag(holder);
}
else
holder=(ViewHolder)convertView.getTag();
holder.text.setText(array[position]);
if(itemStates[position]==0)
{
holder.layout.setBackgroundResource(R.drawable.red_gradient); // item is not clicked/selected yet
}
else
{
holder.button.setBackgroundResource(R.drawable.green_gradient); // item is clicked/selected so change its color
}
final int pos=position;
holder.layout.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
itemStates[pos]=1;
holder.button.setBackgroundResource(R.drawable.green_gradient);
}
});
return convertView;
}
static class ViewHolder
{
TextView text;
LinearLayout layout;
}
}
This will give you behavior like:
Initially all items would be red colored.
if you click on 2nd item,then it will turn green(according to my code).
Now when you scroll and click on any other items,they will keep on turning green from red.
But if you click on "green" colored item,it will again turn red like it is unselected!
you can do something like this
# outside of on create
View prevView;
then in oncreate
lv.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View view, int arg2,
long arg3) {
if (prevView != null) {
prevView.setBackgroundColor(Color.TRANSPARENT);
}
view.setBackgroundColor(Color.RED);
prevView = view;
}
});
I am giving the answer to my own question.
Here's the perfect running code:-
getListView().setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// Toast.makeText(getApplicationContext(), "Item "+position+" is clicked",
// Toast.LENGTH_SHORT).show();
System.out.println("position="+position);
int first = getListView().getFirstVisiblePosition();
System.out.println("first="+first);
int last = getListView().getLastVisiblePosition();
System.out.println("last="+last);
int total = last - first;
System.out.println("total="+total);
if(getListView().isItemChecked(position)){
for(int i = first ; i <= last ; i++){
System.out.println("i="+i);
if(i == position){
Log.w("TAG", "I am in If block");
getListView().getChildAt(i-first).setBackgroundColor(Color.GREEN);
System.out.println("l1="+getListView());
// l.getItemAtPosition(i);
// System.out.println("l position"+l);
}
else{
getListView().getChildAt(i-first).setBackgroundColor(Color.TRANSPARENT);
System.out.println("l2="+getListView());
}
}
}
else{
getListView().getChildAt(position).setBackgroundColor(Color.TRANSPARENT);
}
}
});
A cleaner(?) solution I've successfully used is to create an extension widget of LinearLayout (or whatever root view type you use for your listitem layout) that implements Checkable.
public class CheckableLinearLayout extends LinearLayout implements Checkable {
boolean checked = false;
public CheckableLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CheckableLinearLayout(Context context) {
super(context);
}
#Override
public void setChecked(boolean checked) {
this.checked = checked;
updateView();
}
/* (non-Javadoc)
* #see android.widget.Checkable#isChecked()
*/
#Override
public boolean isChecked() {
return this.checked;
}
/* (non-Javadoc)
* #see android.widget.Checkable#toggle()
*/
#Override
public void toggle() {
this.checked=!this.checked;
updateView();
}
private void updateView() {
if (this.checked) {
//Change to Whatever your checked color should be, maybe expose this as attribute so i't can be changed from xml attribute
setBackgroundResource(R.color.default_background_color);
} else {
setBackgroundDrawable(null);
}
invalidate();
}
}
Then in your item layout xml:
<my.app.widgets.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
..any views for your listitem
<my.app.widgets.CheckableLinearLayout/>
I know it is not possible in Android to scroll grid view horizontally. But what I am doing is adding image buttons dynamically inside horizontal scroll view like this:
public class HorizontalScroller extends Activity {
static int l=0;
private Rect mTempRect = new Rect();
static int r1=0;
static int t=0;
static int b=0;
static int x=0;
static int y=0;
//Button[] b1 = new Button[100];
ImageButton btn[][] = new ImageButton[10][10];
//ImageButton b1 = new ImageButton(this);
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout rl = (LinearLayout)findViewById(R.id.widget92);
LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
for(int i=0;i<4;i++)
{
for(int j=0;j<10;j++)
{System.out.println("helo");
/* l=l+100;
r1=r1+100;
t=t+100;
b=b+100;*/
//button();
//ImageButton btn=new ImageButton(this);
/* Rect r = mTempRect;
r.left=10;
r.top=10;
r.right=10;
r.bottom=10;
btn[i][j].getDrawingRect(r);*/
//btn[i][j].setId(j);
Rect r = mTempRect;
r.set(0,0,0,0);
Rect r2 = mTempRect;
r2.set(0,20,0,20);
btn[i][j]=new ImageButton(this);
btn[i][j]. setBackgroundResource(R.drawable.icon);
btn[i][j].setMinimumWidth(20);
btn[i][j].setMinimumHeight(20);
params1.setMargins(5, 5, 5,5);
rl.addView(btn[i][j],params1);
System.out.println("1="+btn[i][j].getTop());
System.out.println("2="+btn[i][j].getLeft());
System.out.println("3="+btn[i][j].getRight());
System.out.println("4="+btn[i][j].getBottom());
}
}
}
}
but I am getting all image buttons in a single line. How can I implement them in a grid like structure?
Implementing a horizontally scrolling GridView involves copying a few of the Android source code classes into your codebase (AdapterView, AbsListView, GridView, ScrollBarDrawable) and adding in code to handle the horizontal code. This is mainly copying some of the code and changing top to left, bottom to right, etc. The main reason for having to copy instead of extending is the final nature of those classes.
I implemented a horizontally scrolling GridView a while ago and finally got around to pushing to github:
https://github.com/jess-anders/two-way-gridview
You can
use a TableLayout inside a HorizontalScrollView, or
stay with your approach with an horizontal LinearLayout but adding vertical LinearLayouts instead of directly the images. E.g., adding three to four images per vertical LinearLayout in portrait, and redrawing to add only two in landscape.
I would try the TableLayout approach first.
PS1: for next time, try to remove all the non-relevant code (the less code is there, the easier is to understand what you did).
PS2: Remember that System.out is usually redirected to /dev/null and thus lost, so I strongly suggest you to use Log.d instead.
Complete example
Adapt this to the onCreate() method or wherever you need it:
public void horizontalScrollGalleryLayout () {
HorizontalScrollView sv = new HorizontalScrollView(this);
LinearLayout llh = new LinearLayout(this);
llh.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams layoutParamsTV = new LinearLayout.LayoutParams(40, 40);
LinearLayout.LayoutParams layoutParamsLL = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
for (int i=0; i<20; i++) {
LinearLayout llv = new LinearLayout(this);
llv.setOrientation(LinearLayout.VERTICAL);
TestView testView1 = new TestView(this, Color.rgb(i*12, 0, 0));
TestView testView2 = new TestView(this, true, Color.rgb(i*12, i*12, 0));
TestView testView3 = new TestView(this, true, Color.rgb(0, i*12, 0));
llv.addView(testView1, layoutParamsTV);
llv.addView(testView2, layoutParamsTV);
llv.addView(testView3, layoutParamsTV);
llh.addView(llv, layoutParamsLL);
}
sv.addView(llh, layoutParamsLL);
setContentView(sv);
}
I'm using a very simple View as an example:
public class TestView extends View {
Context context;
int color;
public TestView(Context context, int color) {
super(context);
this.context = context;
this.color = color;
}
#Override
public void onDraw (Canvas canvas) {
super.onDraw(canvas);
this.setBackgroundColor(Color.LTGRAY);
Paint paint = new Paint (Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
canvas.drawCircle(20, 20, 20, paint);
}
}
There is a very easy trick.
Rotate the grid view by 270 degree and set number of columns as 2.
Rotate each item to 90 degree (so that the items are displayed as original orientation).
This might be useful for some!!
I have done this way:
activity_main.xml:
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<GridView
android:id="#+id/gridView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</GridView>
</LinearLayout>
</HorizontalScrollView>
MainActivity.java:
GridView gridView = (GridView) findViewById(R.id.gridView);
gridView.setNumColumns(arrayList.size());
GridViewAdapter gridViewAdapter = new GridViewAdapter(mContext, arrayList);
gridView.setAdapter(gridViewAdapter);
// Set dynamic width of Gridview
setDynamicWidth(gridView);
Add below method:
private void setDynamicWidth(GridView gridView) {
ListAdapter gridViewAdapter = gridView.getAdapter();
if (gridViewAdapter == null) {
return;
}
int totalWidth;
int items = gridViewAdapter.getCount();
View listItem = gridViewAdapter.getView(0, null, gridView);
listItem.measure(0, 0);
totalWidth = listItem.getMeasuredWidth();
totalWidth = totalWidth*items;
ViewGroup.LayoutParams params = gridView.getLayoutParams();
params.width = totalWidth;
gridView.setLayoutParams(params);
}
Hope this will help you.
I have already posted this answer here, but both questions are
identical...
There is a nice solution in Android from now on : HorizontalGridView.
1. Gradle dependency
dependencies {
compile 'com.android.support:leanback-v17:23.1.0'
}
2. Add it in your layout
your_activity.xml
<!-- your stuff before... -->
<android.support.v17.leanback.widget.HorizontalGridView
android:layout_width="wrap_content"
android:layout_height="80dp"
android:id="#+id/gridView"
/>
<!-- your stuff after... -->
3. Layout grid element
Create a layout for your grid element ( grid_element.xml ). I have created a simple one with only one button in it.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Button"
android:id="#+id/button" />
</LinearLayout>
4. Create an adapter
Highly inspired by this link : https://gist.github.com/gabrielemariotti/4c189fb1124df4556058
public class GridElementAdapter extends RecyclerView.Adapter<GridElementAdapter.SimpleViewHolder>{
private Context context;
private List<String> elements;
public GridElementAdapter(Context context){
this.context = context;
this.elements = new ArrayList<String>();
// Fill dummy list
for(int i = 0; i < 40 ; i++){
this.elements.add(i, "Position : " + i);
}
}
public static class SimpleViewHolder extends RecyclerView.ViewHolder {
public final Button button;
public SimpleViewHolder(View view) {
super(view);
button = (Button) view.findViewById(R.id.button);
}
}
#Override
public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(this.context).inflate(R.layout.grid_element, parent, false);
return new SimpleViewHolder(view);
}
#Override
public void onBindViewHolder(SimpleViewHolder holder, final int position) {
holder.button.setText(elements.get(position));
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(context, "Position =" + position, Toast.LENGTH_SHORT).show();
}
});
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemCount() {
return this.elements.size();
}
}
5. Initialize it in your activity :
private HorizontalGridView horizontalGridView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_activity);
horizontalGridView = (HorizontalGridView) findViewById(R.id.gridView);
GridElementAdapter adapter = new GridElementAdapter(this);
horizontalGridView.setAdapter(adapter);
}
Use recyclerview with setting its gridlayout as layout manager and set it to horizontal scroll
your recycle view.setLayoutManager(new GridLayoutManager(getActivity(),2, LinearLayoutManager.HORIZONTAL, false))
here 2 is the column span for grid