I am starting to think that short of OpenGL I can't drag around a simple view. Seriously, I have read through 10-15 tutorials for dragging views, and EVERY tutorial shows you how to drag a drawable WITHIN a view, or drag an image INSIDE of an imageview (by matrix transformations). But NONE of the tutorials tell you how you can drag around a simple view in a superview. Its really that simple. I have a superview, I add lets say 10 subviews. Whatever subview my finger is touching drags around. I made an app for iOS http://www.facebook.com/MakeASnowman (hits the app store any day) and am trying to build it for Android. But there is no easy way to do things that are so simple in iOS (two finger rotate, drag around views, etc). This is what I want: I have a custom view that draws an image to its canvas. I then instantiate one or more of those custom views onto a "superview". I can drag/manipulate those views (meaning im not translating the drawables within those views, but the actual views themselves).
Here is my code that has the right manipulations, except it performs them of the drawable within the view:
package com.spentaklabs.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
public class MAView extends View
{
Drawable image;
private String imgName;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public MAView(Context context)
{
super(context);
}
public MAView(Context context,String imgName)
{
this(context);
this.imgName = imgName;
image = context.getResources().getDrawable(R.drawable.hatshairears0);
image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
public MAView(Context context, AttributeSet attrs, int defStyle)
{
super(context,attrs,defStyle);
}
#Override
public void onDraw(Canvas canvas)
{
canvas.save();
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
image.draw(canvas);
canvas.restore();
}
#Override
public boolean onTouchEvent(MotionEvent ev)
{
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action)
{
case MotionEvent.ACTION_DOWN:
{
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE:
{
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP:
{
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL:
{
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP:
{
// Extract the index of the pointer that left the touch sensor
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId)
{
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
}
Here is some code that would let you drag a View anywhere you want on the screen...
package com.matthieu;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.FrameLayout.LayoutParams;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import java.util.ArrayList;
import java.util.Arrays;
public class DragActivity extends Activity implements View.OnTouchListener, AdapterView.OnItemLongClickListener
{
private static final String TAG="DragActivity";
private static final int NOT_DRAGGING = 0;
private static final int DRAGGING = 1;
private int state=NOT_DRAGGING;
private ImageView draggable =null;
private int dragged_position;
float current_x, current_y;
int current_icon = R.drawable.notepad;
private ArrayList<String> names = new ArrayList<String>(Arrays.asList("Matt", "Xiaohui", "Yong", "Hunt", "Andy", "Ivy", "Guanglong", "Zeyan", "Yanxia",
"Chris", "Mark", "Matthieu"));
private ArrayList<Integer> icons = new ArrayList<Integer>(Arrays.asList( R.drawable.glasses, R.drawable.monkey, R.drawable.normal, R.drawable.smile, R.drawable.wink));
private ArrayList<Integer> matching;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setupListContent();
ListView list = (ListView) findViewById(R.id.main_list);
list.setAdapter(new DragListAdapter());
list.setOnItemLongClickListener(this);
list.setOnTouchListener(this);
ImageView image = (ImageView) findViewById(R.id.main_image);
image.setImageResource(current_icon);
}
private void setupListContent() {
matching = new ArrayList<Integer>();
for (int i=0; i<names.size(); i++) {
matching.add((int) (icons.size() * Math.random()));
}
}
#SuppressWarnings("unchecked")
private class DragListAdapter extends ArrayAdapter {
public DragListAdapter() {
super(DragActivity.this, R.layout.list_item, names);
}
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = getLayoutInflater();
row = inflater.inflate(R.layout.list_item, parent, false);
}
row.setDrawingCacheEnabled(true);
TextView name = (TextView) row.findViewById(R.id.item_text);
ImageView icon = (ImageView) row.findViewById(R.id.item_icon);
name.setText(names.get(position));
icon.setImageResource(icons.get(matching.get(position)));
return row;
}
}
private boolean checkOnDropIcon(MotionEvent me) {
ImageView drop_icon = (ImageView) findViewById(R.id.main_image);
Rect icon_rect = new Rect();
drop_icon.getGlobalVisibleRect(icon_rect);
Log.d(TAG, "icon at " + icon_rect.left + "<- ->" + icon_rect.right + ", " +
icon_rect.top + " ^ v" + icon_rect.bottom);
if ((me.getRawX()<icon_rect.left) || (me.getRawX()>icon_rect.right) ||
(me.getRawY()<icon_rect.top) || (me.getRawY()>icon_rect.bottom)) {
return false;
}
else {
return true;
}
}
private void checkOnDrop(MotionEvent me) {
boolean onDropIcon = checkOnDropIcon(me);
ImageView image = (ImageView) findViewById(R.id.main_image);
if ((onDropIcon) && (current_icon==R.drawable.notepad)) {
current_icon = R.drawable.exit;
image.setImageResource(current_icon);
image.invalidate();
return;
}
if ((!onDropIcon) && (current_icon==R.drawable.exit)) {
current_icon = R.drawable.notepad;
image.setImageResource(current_icon);
image.invalidate();
return;
}
}
public boolean onTouch(View view, MotionEvent me) {
if (state == NOT_DRAGGING) {
// get the position of the touch so we know where to place the dragging item if it is a long press
current_x = me.getRawX();
current_y = me.getRawY();
return false;
}
else {
FrameLayout frame = (FrameLayout) findViewById(R.id.drag_space);
if (me.getAction()==MotionEvent.ACTION_UP) {
frame.removeAllViews();
draggable=null;
frame.setVisibility(View.GONE);
state=NOT_DRAGGING;
// check if we dropped a name
if (checkOnDropIcon(me)) {
names.remove(dragged_position);
matching.remove(dragged_position);
ListView list = (ListView) findViewById(R.id.main_list);
DragListAdapter adapter = (DragListAdapter) list.getAdapter();
adapter.notifyDataSetChanged();
}
// restore the icon
ImageView image = (ImageView) findViewById(R.id.main_image);
current_icon = R.drawable.notepad;
image.setImageResource(current_icon);
image.invalidate();
}
if (me.getAction()==MotionEvent.ACTION_MOVE) {
int frame_position[] = new int[2];
frame.getLocationOnScreen(frame_position);
draggable.setPadding(
(int) me.getRawX()-frame_position[0]-(draggable.getDrawable().getIntrinsicWidth()/2),
(int) me.getRawY()-frame_position[1]-(draggable.getDrawable().getIntrinsicHeight()/2),
0, 0);
draggable.invalidate();
checkOnDrop(me);
}
return true;
}
}
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
if (state == DRAGGING) {
Log.d(TAG, "already have an object moving... ?");
return false;
}
FrameLayout frame = (FrameLayout) findViewById(R.id.drag_space);
int frame_position[] = new int[2];
frame.getLocationOnScreen(frame_position);
// setup everything for dragging
state = DRAGGING;
dragged_position = i;
draggable = new ImageView(this);
Bitmap bm = view.getDrawingCache();
draggable.setImageBitmap(bm);
draggable.setAlpha(150);
draggable.setScaleType(ImageView.ScaleType.CENTER);
draggable.setDrawingCacheEnabled(true);
draggable.setPadding((int) current_x-frame_position[0]-(bm.getWidth()/2), (int) current_y-frame_position[1]-(bm.getHeight()/2), 0, 0);
frame.setVisibility(View.VISIBLE);
frame.addView(draggable, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
return true;
}
}
Here is the main.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main_frame"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ListView
android:id="#+id/main_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
/>
<ImageView
android:id="#+id/main_image"
android:layout_width="fill_parent"
android:layout_height="32sp"
/>
</LinearLayout>
<FrameLayout
android:id="#+id/drag_space"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="gone"/>
</FrameLayout>
And the list_item.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="horizontal">
<ImageView
android:id="#+id/item_icon"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_margin="3sp"/>
<TextView
android:id="#+id/item_text"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="1"/>
</LinearLayout>
Hope that helps.
Related
This what I have achieved so far
Image adapter
public class ImageAdapter extends BaseAdapter {
private Context mContext;
TextView text;
ImageView imageView;
// Constructor
public ImageAdapter(Context c) {
mContext = c;
}
public int getCount() {
return mThumbIds.length;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
View view = View.inflate(mContext, R.layout.grid_view, null);
RelativeLayout rl = (RelativeLayout) view.findViewById(R.id.relaGrid);
ImageView image = (ImageView) rl.findViewById(R.id.chooseImage);
TextView text = (TextView) rl.findViewById(R.id.chooseText);
image.setImageResource(mThumbIds[position]);
text.setText(mThumbTxt[position]);
Typeface customFont = Typeface.createFromAsset(mContext.getAssets(), "fonts/faruma.ttf");
text.setTypeface(customFont);
return rl;
}
// Keep all Images in array
public Integer[] mThumbIds = {
R.drawable.ic_action_gallery, R.drawable.ic_action_gallery,
R.drawable.ic_action_gallery, R.drawable.ic_action_gallery,
R.drawable.ic_action_gallery, R.drawable.ic_action_gallery,
R.drawable.ic_action_gallery, R.drawable.ic_action_gallery
};
private String[] mThumbTxt = {
"Morning", "Evening", "Test deler", "Hepsi", "Test 2",
"Test 3", "Test 4", "Test 5"
};
}
View
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/relaGrid"
android:descendantFocusability="blocksDescendants"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
card_view:cardCornerRadius="5dp"
card_view:cardElevation="6dp"
android:translationZ="5dp"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
card_view:cardUseCompatPadding="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="onCardclick"
android:clickable="true"
android:paddingBottom="10dp">
<ImageView
android:id="#+id/chooseImage"
android:layout_width="match_parent"
android:layout_height="85dp">
</ImageView>
<TextView android:id="#+id/chooseText"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:textSize="18sp"
android:layout_below="#+id/chooseImage"
android:gravity="center"
android:supportsRtl="true"
android:singleLine="true"
android:paddingRight="5dp"
android:paddingLeft="5dp"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever">
</TextView>
</RelativeLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>
I am trying to get ripple effect on cardview with onclick listener start another intent. but when ripple effect is enabled, onclick event is not working. I just only see ripple effect only. Onclick listener doesn't work. How to achieve both? Please help
Full MainActivity
package com.mycompany.alun;
import android.content.*;
import android.database.sqlite.*;
import android.graphics.*;
import android.os.*;
import android.support.v7.app.*;
import android.support.v7.widget.*;
import android.support.v7.widget.SearchView;
import android.util.*;
import android.view.*;
import android.widget.*;
import android.widget.AdapterView.*;
import java.io.*;
import android.support.v7.widget.Toolbar;
import android.app.*;
import android.support.v4.view.*;
public class MainActivity extends AppCompatActivity {
SQLiteDatabase sqLiteDatabase;
DatabaseHelper mDBHelper;
TextView APP_NAME;
SearchView searchView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDBHelper = new DatabaseHelper(this);
//Check exists database
File database = getApplicationContext().getDatabasePath(DatabaseHelper.DBNAME);
if(false == database.exists()) {
mDBHelper.getReadableDatabase();
//Copy db
if(copyDatabase(this)) {
Toast.makeText(this, "Copy database succes", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Copy data error", Toast.LENGTH_SHORT).show();
return;
}
}
// Custom title
TextView textCustomTitle = (TextView) findViewById(R.id.appName);
Typeface customFont = Typeface.createFromAsset(this.getAssets(), "fonts/faruma.ttf");
textCustomTitle.setTypeface(customFont);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this));
if (!isTaskRoot()) {
finish();
return;
}
}
public void onCardclick(AdapterView<?> a, View v, int position, long id) {
switch(position)
{
case 0:
Intent newActivity = new Intent(MainActivity.this,ViewDataActivity.class);
startActivity(newActivity);
break;
case 1:
Intent newActivity2 = new Intent(MainActivity.this,MorningActivity.class);
startActivity(newActivity2);
break;
case 2:
Intent new1Activity = new Intent(MainActivity.this,ViewDataActivity.class);
startActivity(new1Activity);
break;
case 3:
Bitmap src = BitmapFactory.decodeResource(getResources(), R.drawable.daily_life); // the original file yourimage.jpg i added in resources
Bitmap dest = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888);
String yourText = "My custom Text adding to Image";
Canvas cs = new Canvas(dest);
Paint tPaint = new Paint();
tPaint.setTextSize(35);
tPaint.setColor(Color.BLUE);
tPaint.setStyle(Paint.Style.FILL);
cs.drawBitmap(src, 0f, 0f, null);
float height = tPaint.measureText("yY");
float width = tPaint.measureText(yourText);
float x_coord = (src.getWidth() - width)/2;
cs.drawText(yourText, x_coord, height+15f, tPaint); // 15f is to put space between top edge and the text, if you want to change it, you can
try {
dest.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(new File("/sdcard/ImageAfterAddingText.jpg")));
// dest is Bitmap, if you want to preview the final image, you can display it on screen also before saving
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case 4:
new CustomToast(getApplicationContext(), getApplicationContext().getString(R.string.add_favorites));
break;
case 5:
Intent searchActivity = new Intent(MainActivity.this,SearchActivity.class);
startActivity(searchActivity);
break;
default:
Toast.makeText(MainActivity.this, "Wrong Input", Toast.LENGTH_LONG).show();
}
}
#Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.nav_menu, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
return super.onCreateOptionsMenu(menu);
}
public void getInput(String searchText)
{
Intent in = new Intent(this, SearchActivity.class);
in.putExtra("SEARCH", searchText);
startActivity(in);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_favorites) {
Intent newActivity = new Intent(MainActivity.this,FavoritesActivity.class);
startActivity(newActivity);
}
if (item.getItemId() == R.id.action_search) {
Intent newActivity = new Intent(MainActivity.this,SearchActivity.class);
startActivity(newActivity);
}
return super.onOptionsItemSelected(item);
}
private boolean copyDatabase(Context context) {
try {
InputStream inputStream = context.getAssets().open(DatabaseHelper.DBNAME);
String outFileName = DatabaseHelper.DBLOCATION + DatabaseHelper.DBNAME;
Log.d("ViewDataActivity", "outFileName : "+outFileName);
OutputStream outputStream = new FileOutputStream(outFileName);
byte[]buff = new byte[1024];
int length = 0;
while ((length = inputStream.read(buff)) > 0) {
outputStream.write(buff, 0, length);
}
outputStream.flush();
outputStream.close();
Log.w("ViewDataActivity","DB copied");
return true;
}catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
Inside adapter method getView()
CardView cardView = view.findViewById(R.id.card_view_id_from_grid_view)
cardView.setOnClickListener(...)
EDIT
interface CustomClick{
void onClick(int position)
}
private CustomClick customClick;
// Constructor
public ImageAdapter(Context c, CustomClick customClick) {
mContext = c;
this.customClick = customClick;
}
cardView.setOnClickListener(view -> customClick.onClick(position))
MainActivity implements CustomClick
new ImageAdapter(this, this)
First of all assign a specific ID to it.
android:id="#+id/btnCardView"
And yes have it clickable="true".
Then the usual work, go to that respective activity.
Then initialise your card view there.
private CardView cardview;
cardview = findViewById(R.id.btnCardView);
btnCardView.setOnClickListener(new ....) {
//Your function here
}
My intention is to say add the onClickListener to the CardView itself instead of the Relative layout in it.
And just a note that ripple effect won't work on older versions of Android.
I have an Adapter that extends RecyclerView.Adapter. Each line of adapter is a custom FrameLayout with GestureDetector implemented. The FrameLayout xml file has 2 RelativeLayout.
In this FrameLayout I redirect X and Y positions to the RelativeLayout that is in the foreground.
In each RelativeLayout I have a setOnClickListener. This is because if user clicks the RelativeLayout that is on the foreground, an activity is started. If the user scrolls the former, the latter is available to click.
What I'm facing is:
Implementing setOnClickListener on these layouts I lost view scroll feature of my custom FrameLayout. And I need the scroll feature and clickListener for each view.
This is my xml
file:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/background_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/work_order_deleted_item_background_color">
...
</RelativeLayout>
<RelativeLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:visibility="invisible"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin">
...
</RelativeLayout>
</FrameLayout>
My custom FrameLayout:
public class SwipeView extends FrameLayout implements GestureDetector.OnGestureListener{
private RelativeLayout container;
private RelativeLayout backgroundContainer;
private GestureDetector detector;
private float posX;
private float posY;
private float lastTouchX;
private float lastTouchY;
private float xLimit;
private int mActivePointerId;
public SwipeView(#NonNull Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.adapter_work_order, this);
initViews(context, null);
xLimit = 200.0f;
mActivePointerId = INVALID_POINTER_ID;
detector = new GestureDetector(this);
}
public SwipeView(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
initViews(context, attrs);
xLimit = 200.0f;
mActivePointerId = INVALID_POINTER_ID;
detector = new GestureDetector(this);
}
public SwipeView(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initViews(context, attrs);
xLimit = 200.0f;
mActivePointerId = INVALID_POINTER_ID;
detector = new GestureDetector(this);
}
private void initViews(Context context, AttributeSet attrs){
this.container = this.findViewById(R.id.container);
this.backgroundContainer = this.findViewById(R.id.background_container);
this.checkboxDownload = this.findViewById(R.id.checkbox_download);
this.textviewWo = this.findViewById(R.id.textview_wo);
this.downloadedWo = this.findViewById(R.id.textview_downloaded_wo);
this.textviewTime = this.findViewById(R.id.textview_time);
this.textviewField1 = this.findViewById(R.id.textview_field_1);
this.textviewField2 = this.findViewById(R.id.textview_field_2);
this.textviewField3 = this.findViewById(R.id.textview_field_3);
this.deleteLabel = this.backgroundContainer.findViewById(R.id.delete_wo);
}
#Override
public boolean onTouchEvent(MotionEvent event){
final int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN: {
final int pointerIndex = MotionEventCompat.getActionIndex(event);
final float x = MotionEventCompat.getX(event, pointerIndex);
final float y = MotionEventCompat.getY(event, pointerIndex);
// Remember where we started (for dragging)
lastTouchX = x;
lastTouchY = y;
// Save the ID of this pointer (for dragging)
mActivePointerId = MotionEventCompat.getPointerId(event, 0);
if(x > container.getRight() + container.getX()){
Log.e("ERROR", "TOUCHED OUTSIDE");
}
else {
Log.e("ERROR", "TOUCHED INSIDE: ");
}
break;
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
final int pointerIndex = MotionEventCompat.findPointerIndex(event, mActivePointerId);
final float x = MotionEventCompat.getX(event, pointerIndex);
final float y = MotionEventCompat.getY(event, pointerIndex);
// Calculate the distance moved
final float dx = x - lastTouchX;
final float dy = y - lastTouchY;
posX += dx;
posY += dy;
if(posX <= -xLimit){
posX -= dx;
}
if(posX > 0.0f){
posX = 0.0f;
}
invalidate();
// Remember this touch position for the next move event
lastTouchX = x;
lastTouchY = y;
container.setX(posX);
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
}
return true;
}
#Override
public boolean onDown(MotionEvent motionEvent) {
return true;
}
#Override
public void onShowPress(MotionEvent motionEvent) {
}
#Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
return true;
}
#Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return true;
}
#Override
public void onLongPress(MotionEvent motionEvent) {
}
#Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float velocityX, float velocityY) {
return true;
}
}
And this code is inside my adapter:
holder.container.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//onItemClickListener.onItemClick(workOrder);
Log.e("ERROR", "FOREGROUND CLICKED");
}
});
holder.backgroundContainer.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view){
Log.e("ERROR", "BACKGROUND CLICKED");
}
});
Finally this is the code that is inside my Activity:
((MyAdapter) recyclerview.getAdapter())
.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
#Override
public void onItemClick(Item item) {
if(!presenter.getDownloadAvailability()) {
...
}
}
});
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp"
android:fillViewport="false">
<RelativeLayout
android:id="#+id/background_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/work_order_deleted_item_background_color">
...
</RelativeLayout>
</ScrollView>
I'm trying to take a RecyclerView with a StaggeredGridLayout and make it a fixed height by having it measure the views and set the height dynamically. I'm overriding the onMeasure(), but it does not always seem to measure correctly. I'd say it works about 50% of the time. The other 50% of the time it under measures it. I think it has to do with when the text wraps in the view_tile_small.xml, but I'm not sure.
Fragment
public class AtTheMuseumFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener{
//Odds n Ends Variables
private MapFragment mapFragment;
private FragmentTransaction fragmentTransaction;
private User user = new User();
private MuseumCollection museumCollection;
private Context context;
//Story Grid Adapter Variables
private AtTheMuseumAdapter nearMeAdapter;
private TileFactory tileFactory;
//Interfaces
private OnFragmentChangeListener changeListener;
#Bind(R.id.stories_list_view) RecyclerView storiesListView;
#Bind(R.id.swipe_container) SwipeRefreshLayout swipeRefreshLayout;
public static AtTheMuseumFragment newInstance(MuseumCollection museumCollection) {
AtTheMuseumFragment fragment = new AtTheMuseumFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
public AtTheMuseumFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
museumCollection = ((MainActivity) getActivity()).getMuseumCollection();
context = getActivity().getApplicationContext();
tileFactory = new TileFactory();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_museum, container, false);
ButterKnife.bind(this, view);
//Sets up the layoutManager to the Mason View
storiesListView.setLayoutManager(new MeasuredStaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
//Sets up The map
try{
fragmentTransaction = getChildFragmentManager().beginTransaction();
mapFragment = MapFragment.newInstance(MapFragment.MUSEUM);
fragmentTransaction.add(R.id.museum_map, mapFragment).commit();
}catch (Exception e){
Log.d("Map - Initial Inflate:", e.getMessage());
}
//Sets up the swipe to refresh jawn
swipeRefreshLayout.setOnRefreshListener(this);
setNearMeAdapter();
return view;
}
#Override
public void onResume(){
super.onResume();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
changeListener = (OnFragmentChangeListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
changeListener = null;
}
/**
* Loads the adapter once the data is ready
*
*/
public void setNearMeAdapter(){
List<MuseumObject> newList = museumCollection.getObjectsNearUser(User.position, 4);
List<Tile> tiles = tileFactory.createAtMuseumFeed(newList, true);
nearMeAdapter = new AtTheMuseumAdapter(context, tiles, getActivity());
storiesListView.setAdapter(nearMeAdapter);
}
/**
* Refreshes Adapter with new data
*
*/
public void refreshNearMeAdapter(){
//TODO CHANGE THIS TO LOCATION BASED WHEN TIME IS RIGHT - Peter
//nearMeAdapter.setNewData(MuseumCollection.getObjectsNearUser(User.position, 4));
List<MuseumObject> newList = museumCollection.getRandomOrder();
nearMeAdapter.setNewData(tileFactory.createAtMuseumFeed(newList,false));
}
/**
* Adds past data to the Adapter
*
*/
public void loadPastObjects(){
//TODO MAKE THIS NOT ONLY LOAD RANDOM DATA - Peter
List<MuseumObject> newList = museumCollection.getRandomOrder();
nearMeAdapter.addNewData(tileFactory.createAtMuseumFeed(newList, false));
nearMeAdapter.notifyDataSetChanged();
}
#Override
public void onRefresh() {
user.updateUserLocation();
refreshNearMeAdapter();
mapFragment.refreshMap(museumCollection.getObjectFeed());
swipeRefreshLayout.setRefreshing(false);
}
public interface OnFragmentChangeListener {
void onFragmentChange(String fragment);
}
#OnClick(R.id.explore_map)
public void exploreMap(){
changeListener.onFragmentChange("map");
}
#OnClick(R.id.load_more)
public void loadMore(){
loadPastObjects();
}
}
MeasuredStaggeredGridLayoutManager
public class MeasuredStaggeredGridLayoutManager extends StaggeredGridLayoutManager {
public MeasuredStaggeredGridLayoutManager(int spanCount, int orientation) {
super(spanCount, orientation);
}
private int[] mMeasuredDimension = new int[2];
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
int heightR = 0;
int heightL = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
if(i % 2 == 0){
heightL += mMeasuredDimension[1];
}else{
heightR += mMeasuredDimension[1];
}
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
if(heightL != 0 || heightR != 0){
height = (heightL > heightR) ? heightL : heightR;
}
//TODO come up with a better way to fix the slightly wrong height
// must be not accounting for padding or margin or something - Peter
height += (20 * (getItemCount() / 2)) + 5;
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
}
}
AtTheMuseumAdapter
public class AtTheMuseumAdapter extends RecyclerView.Adapter<AtTheMuseumAdapter.MuseumStoriesViewHolder> {
private List<Tile> tiles;
private LayoutInflater inflater;
private AdapterCallback mListener;
private Context context;
public AtTheMuseumAdapter(Context context, List<Tile> tiles, Activity activity) {
this.tiles = tiles;
this.context = context;
inflater = LayoutInflater.from(this.context);
//Sets up interface between Stock Adapter and Fragment
try {
this.mListener = ((AdapterCallback) activity);
} catch (ClassCastException e) {
throw new ClassCastException("Fragment must implement AdapterCallback.");
}
}
#Override
public MuseumStoriesViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = inflater.inflate(R.layout.view_tile_small, viewGroup, false);
MuseumStoriesViewHolder holder = new MuseumStoriesViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(MuseumStoriesViewHolder holder, int position) {
Tile currentTile = tiles.get(position);
holder.title.setText(currentTile.getTitle());
holder.desc.setText(currentTile.getDescription());
holder.type.setText(currentTile.getObjectTypeName());
//Using Picasso since it handles caching and all that jazz
Picasso.with(context)
.load(currentTile.getImg())
.into(holder.img);
}
#Override
public int getItemCount() {
return tiles.size();
}
public void setNewData(List<Tile> newItems){
tiles = newItems;
notifyDataSetChanged();
}
public void addNewData(final List<Tile> newItems){
tiles.addAll(newItems);
notifyDataSetChanged();
}
class MuseumStoriesViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView type,title,desc;
public ImageView img;
public MuseumStoriesViewHolder(View itemView) {
super(itemView);
//Tried Butterknife, but it doesn't seem like it was working in the view holder. - Peter
type = (TextView) itemView.findViewById(R.id.small_box_type);
title = (TextView) itemView.findViewById(R.id.small_box_title);
desc = (TextView) itemView.findViewById(R.id.small_box_desc);
img = (ImageView) itemView.findViewById(R.id.small_box_image);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
Tile t = tiles.get(getPosition());
switch (t.getObjectTypeName()){
case Tile.OBJECT_NAME:
mListener.onObjectClick(t.getObjectID());
break;
case Tile.TOUR_NAME:
mListener.onTourCLick(t.getTourID());
break;
case Tile.STORY_NAME:
mListener.onStoryClick(t.getObjectID(), t.getStoryID());
break;
}
}
}
public interface AdapterCallback {
public void onObjectClick(String objectID);
public void onStoryClick(String objectID, String storyID);
public void onTourCLick(String tourID);
}
}
view_tile_small.xml
<?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="wrap_content"
android:layout_margin="#dimen/margin_small"
android:background="#color/small_box_background_color">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--TODO Make layout_height wrap contenet -->
<ImageView
android:id="#+id/small_box_image"
android:layout_width="match_parent"
android:layout_height="150dp"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:maxHeight="150dp"
android:background="#color/transparent"/>
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_gravity="right"
android:src="#drawable/abc_btn_rating_star_off_mtrl_alpha"
/>
<TextView
android:id="#+id/small_box_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/margin_normal"
android:paddingBottom="#dimen/margin_normal"
android:textSize="#dimen/font_small"
android:textColor="#color/font_white"
android:background="#drawable/small_box_text_bottom_border"
android:layout_gravity="bottom"
android:text="Object Story"
/>
</FrameLayout>
<TextView
android:id="#+id/small_box_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/margin_larger"
android:layout_marginBottom="#dimen/margin_normal"
android:layout_marginRight="#dimen/margin_larger"
android:layout_marginTop="#dimen/margin_larger"
android:textSize="#dimen/font_large"
android:textColor="#color/font_black"
android:text="Sample Text Here"
/>
<TextView
android:id="#+id/small_box_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/margin_larger"
android:layout_marginBottom="#dimen/margin_larger"
android:textSize="#dimen/font_normal"
android:textColor="#color/font_black"
android:textStyle="italic"
android:text="Sample Text Here"
/>
</LinearLayout>
fragment_museum
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.bluecadet.android.nasm.ui.AtTheMuseumFragment"
android:id="#+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="100dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:descendantFocusability="blocksDescendants"
>
<TextView
android:id="#+id/museum_header"
style="#style/header"
android:text="#string/museum_header"
android:layout_margin="#dimen/margin_larger"
android:elevation="8dp"
/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="275dp"
android:elevation="2dp"
>
<FrameLayout
android:id="#+id/museum_map"
android:layout_height="fill_parent"
android:layout_width="match_parent"
/>
<include
layout="#layout/view_explore_map" />
</FrameLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/stories_list_view"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/margin_normal"
android:layout_marginLeft="#dimen/margin_small"
android:layout_marginRight="#dimen/margin_small"
android:layout_marginTop="#dimen/margin_normal"
android:stretchMode="columnWidth"
/>
<Button
android:id="#+id/load_more"
style="#style/home_button"
android:gravity="center"
android:text="#string/button_load_more"
/>
</LinearLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
ViewTreeObserver code I'm playing with now. It's in Fragment.
mTopStoriesListView.setLayoutManager(new NewMeasuredStaggeredLayoutManager(2, StaggeredGridLayoutManager.VERTICAL, mTopStoriesListView));
mTopStoriesListView.setNestedScrollingEnabled(false);
//Testing Issue 54
final ViewTreeObserver viewTreeObserver = mTopStoriesListView.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mTopStoriesListView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int l = 0,r = 0;
for(int i = 0 ; i < mNearMeAdapter.getItemCount(); i++){
int h = mTopStoriesListView.getLayoutManager().findViewByPosition(i).getHeight();
ViewGroup.MarginLayoutParams layout = (ViewGroup.MarginLayoutParams) mTopStoriesListView.getLayoutManager()
.findViewByPosition(i).getLayoutParams();
int t = layout.topMargin;
int b = layout.bottomMargin;
if(i % 2 == 0){
l += h + t + b;
}else{
r += h + t + b;
}
}
int viewHeight = (l > r) ? l : r;
mTopStoriesListView.getLayoutParams().height = viewHeight;
Log.d("TAG", String.valueOf(viewHeight));
}
});
}
//END TEST
Have you look at ViewTreeObserver ?
I got a similar problem on a passed project and I have found it more reliable than onMesure to dynamically get Layout properties
You can go through it from here : http://developer.android.com/reference/android/view/ViewTreeObserver.html
I have spent a good month trying to do all of this together.
Draw Bitmap to screen
On create move Bitmap down at a constant rate
Stop the Bimap when it reaches the bottom
Allow the Bitmap to be clicked on while it moves (a.k.a. not translation animation)
Thanks for you for helping put this all together in advance.
Here is my attempt:
package com.example.animating;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
public class FirstView extends View {
private int screenW;
private int screenH;
private float drawScaleW;
private float drawScaleH;
int moveRate = 10, dot1y, dot1x;
private Context myContext;
private Bitmap dot;
private boolean dotSinking = true, gameOver = false;
public FirstView(Context context) {
super(context);
myContext = context;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
dot = BitmapFactory.decodeResource(myContext.getResources(), R.drawable.dot);
screenW = w;
screenH = h;
drawScaleW = (float) screenW / 800;
drawScaleH = (float) screenH / 600;
dot1y = (int) (475*drawScaleH);
dot1x = (int) (55*drawScaleW);
}
public void run() {
if (!gameOver) {
animateDot();
}
}
#SuppressLint("DrawAllocation")
#Override
protected void onDraw(Canvas canvas) {
Paint redPaint = new Paint();
redPaint.setColor(Color.RED);
canvas.drawBitmap(dot, dot1x, dot1y, null);
}
private void animateDot(){
if (dotSinking) {
dot1y -= moveRate;
}
}
}
To answer first part of your question
activity_main.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"
tools:context=".MainActivity" >
<LinearLayout
android:id="#+id/textView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/button1"
/>
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="Button" />
</RelativeLayout>
MainActivity class
public class MainActivity extends Activity
{
FirstView dv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dv = new FirstView(this);
setContentView(R.layout.activity_main);
final Button b= (Button) findViewById(R.id.button1);
b.setOnClickListener(new OnClickListener()
{
public void onClick(View v) {
// TODO Auto-generated method stub
LinearLayout rl= (LinearLayout ) findViewById(R.id.textView1);
rl.addView(dv);
b.setVisibility(View.INVISIBLE);
}
});
}
public class FirstView extends View {
private int screenW;
private int screenH;
private float drawScaleW;
private float drawScaleH;
int moveRate = 10, dot1y, dot1x;
private Context myContext;
private Bitmap dot;
private boolean dotSinking = true, gameOver = false;
public FirstView(Context context) {
super(context);
myContext = context;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
dot = BitmapFactory.decodeResource(myContext.getResources(), R.drawable.ic_launcher);
screenW = w;
screenH = h;
drawScaleW = (float) screenW / 800;
drawScaleH = (float) screenH / 600;
dot1y = 0+dot.getHeight();
dot1x = w/2-(dot.getWidth()/2);
}
public void run() {
if (!gameOver) {
animateDot();
invalidate();
}
}
#Override
protected void onDraw(Canvas canvas) {
Paint redPaint = new Paint();
redPaint.setColor(Color.RED);
canvas.drawBitmap(dot, dot1x, dot1y, null);
run();
}
private void animateDot(){
if (dotSinking) {
if(dot1y<screenH-dot.getHeight())
dot1y += moveRate;
}
}
}
I have a button at the bottom of the screen. When clicked (button becomes invisible) the image moves from the top to the height of the layout. When it reaches the layout height it stops.
I don't know how to add a click listener for the image moving.
Answered first part of the question. Modify the above according to your needs.
you should use the invalidate() method inside your onDraw() to make it redraw your bitmap's new position. and to make it stop when i reaches the bottom use the setbounds method for your bitmap inside an if sentence telling the bitmap that when the distance with the screec is 0 make it stop. you should set an onclicklistener to your bitmap.to.make it.clickable . hope this helps you!
I've got this code that makes the image turn into a circle using a ListView. But as seen in the image, it dies when it goes to the end of the photo (the last few pixels are carried on when there not supposed to).
I don't want the photo to take up the whole page, just the size of the photo. Here's the code and XML:
Stream item xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</RelativeLayout>
activity stream xml:
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_list"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:divider="#null"
android:dividerHeight="0px"
android:drawSelectorOnTop="true"
tools:context=".StreamActivity" />
main code:
public class StreamActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stream);
StreamAdapter adapter = new StreamAdapter(this);
((ListView) findViewById(R.id.main_list)).setAdapter(adapter);
adapter.add(new StreamItem(this, R.drawable.testimage1, null, null));
adapter.add(new StreamItem(this, R.drawable.testimage1, null, null));
adapter.add(new StreamItem(this, R.drawable.testimage1, null, null));
adapter.add(new StreamItem(this, R.drawable.testimage1, null, null));
}
class StreamItem {
final Bitmap mBitmap;
StreamItem(Context c, int resid, String line1, String line2) {
mBitmap = BitmapFactory.decodeResource(c.getResources(), resid);
}
}
class StreamDrawable extends Drawable {
private static final boolean USE_VIGNETTE = true;
private final float mCornerRadius;
private final RectF mRect = new RectF();
private final BitmapShader mBitmapShader;
private final Paint mPaint;
private final int mMargin;
StreamDrawable(Bitmap bitmap, float cornerRadius, int margin) {
mCornerRadius = cornerRadius;
mBitmapShader = new BitmapShader(bitmap,
Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setShader(mBitmapShader);
mMargin = margin;
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mRect.set(mMargin, mMargin, bounds.width() - mMargin, bounds.height() - mMargin);
if (USE_VIGNETTE) {
RadialGradient vignette = new RadialGradient(
mRect.centerX(), mRect.centerY() * 1.0f / 0.7f, mRect.centerX() * 1.3f,
new int[] { 0, 0, 0x7f000000 }, new float[] { 0.0f, 0.7f, 1.0f },
Shader.TileMode.CLAMP);
Matrix oval = new Matrix();
oval.setScale(1.0f, 0.7f);
vignette.setLocalMatrix(oval);
mPaint.setShader(
new ComposeShader(mBitmapShader, vignette, PorterDuff.Mode.SRC_OVER));
}
}
#Override
public void draw(Canvas canvas) {
canvas.drawRoundRect(mRect, mCornerRadius, mCornerRadius, mPaint);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
#Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
}
class StreamAdapter extends ArrayAdapter<StreamItem> {
private static final int CORNER_RADIUS =100; // dips
private static final int MARGIN = 1; // dips
private final int mCornerRadius;
private final int mMargin;
private final LayoutInflater mInflater;
public StreamAdapter(Context context) {
super(context, 0);
final float density = context.getResources().getDisplayMetrics().density;
mCornerRadius = (int) (CORNER_RADIUS * density + 0.5f);
mMargin = (int) (MARGIN * density + 0.5f);
mInflater = LayoutInflater.from(getContext());
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewGroup view = null;
if (convertView == null) {
view = (ViewGroup) mInflater.inflate(R.layout.stream_item, parent, false);
} else {
view = (ViewGroup) convertView;
}
StreamItem item = getItem(position);
StreamDrawable d = new StreamDrawable(item.mBitmap, mCornerRadius, mMargin);
view.setBackgroundDrawable(d);
int w = item.mBitmap.getWidth();
int h = item.mBitmap.getHeight();
float ratio = w / (float) h;
LayoutParams lp = view.getLayoutParams();
lp.width = getContext().getResources().getDisplayMetrics().widthPixels;
lp.height = (int) (lp.width / ratio);
return view;
}
}
}