Hello,
I create that thread to present you a tutorial aiming to learn you how to create a Running Man Game Animation on Android. You can discover the tutorial in video on Youtube :
Create a Running Man Game Animation on Android
Making a Running Man Animation on Android is a great way to learn how to work with Bitmaps, Thread and SurfaceView. First thing to make a Running Man Game Animation is to have a character to animate. For that, we will use the following sprite sheet :
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Like you can see, our character sprite sheet has 8 frames. Each frame show the character in a different position when he runs.
For our animation, we are going to create a custom GameView extending the SurfaceView class and implementing the Runnable interface. First, we need to define some properties like the game thread, the surface holder, the canvas where running man will be drawn, the bitmap used to load the sprite sheet and some parameters to customize the running man animation like the speed, the size or the position of the man on the screen :
Code:
class GameView extends SurfaceView implements Runnable {
private Thread gameThread;
private SurfaceHolder ourHolder;
private volatile boolean playing;
private Canvas canvas;
private Bitmap bitmapRunningMan;
private boolean isMoving;
private float runSpeedPerSecond = 500;
private float manXPos = 10, manYPos = 10;
private int frameWidth = 230, frameHeight = 274;
private int frameCount = 8;
private int currentFrame = 0;
private long fps;
private long timeThisFrame;
private long lastFrameChangeTime = 0;
private int frameLengthInMillisecond = 50;
// ...
}
To draw correctly the good frame for the running man, we need two Rectangle instances. One used to define the current frame in the sprite sheet and an other to define where to draw the current frame on the screen :
Code:
private Rect frameToDraw = new Rect(0, 0, frameWidth, frameHeight);
private RectF whereToDraw = new RectF(manXPos, manYPos,
manXPos + frameWidth, frameHeight);
On the GameView constructor, we get the surface holder and then, we load the sprite sheet into the bitmapRunningMan variable. We apply a scale transformation according values defined in frameWidth and frameHeight parameters :
Code:
public GameView(Context context) {
super(context);
ourHolder = getHolder();
bitmapRunningMan = BitmapFactory.decodeResource(getResources(),
R.drawable.running_man);
bitmapRunningMan = Bitmap.createScaledBitmap(bitmapRunningMan,
frameWidth * frameCount, frameHeight, false);
}
Now, it's time to make the event loop for animation inside the run method overrided from Runnable interface :
Code:
@Override
public void run() {
while (playing) {
long startFrameTime = System.currentTimeMillis();
update();
draw();
timeThisFrame = System.currentTimeMillis() - startFrameTime;
if (timeThisFrame >= 1) {
fps = 1000 / timeThisFrame;
}
}
}
Note that we animate the character while the playing variable is set to true. Like usual in a game, we update the elements and then we draw before to calculate frame per seconds. The update method is just used here to move the man positions in X and Y. Note that when the man reach the end of the screen horizontally or vertically, we set its position to the left or top of the screen :
Code:
public void update() {
if (isMoving) {
manXPos = manXPos + runSpeedPerSecond / fps;
if (manXPos > getWidth()) {
manYPos += (int) frameHeight;
manXPos = 10;
}
if (manYPos + frameHeight > getHeight()) {
manYPos = 10;
}
}
}
Before to write the draw method, we need to define a method to manage the current frame to display for the character. We change the current frame only when he have ended the frame duration :
Code:
public void manageCurrentFrame() {
long time = System.currentTimeMillis();
if (isMoving) {
if (time > lastFrameChangeTime + frameLengthInMillisecond) {
lastFrameChangeTime = time;
currentFrame++;
if (currentFrame >= frameCount) {
currentFrame = 0;
}
}
}
frameToDraw.left = currentFrame * frameWidth;
frameToDraw.right = frameToDraw.left + frameWidth;
}
And now, we define the draw method :
Code:
public void draw() {
if (ourHolder.getSurface().isValid()) {
canvas = ourHolder.lockCanvas();
canvas.drawColor(Color.WHITE);
whereToDraw.set((int) manXPos, (int) manYPos, (int) manXPos
+ frameWidth, (int) manYPos + frameHeight);
manageCurrentFrame();
canvas.drawBitmap(bitmapRunningMan, frameToDraw, whereToDraw, null);
ourHolder.unlockCanvasAndPost(canvas);
}
}
First, we check if the surface is valid. Then we lock the canvas and we draw the character current frame. Last, we unlock the canvas and post it on the Surface View. Finally, we define two methods to pause or resume the running man animation :
Code:
public void pause() {
playing = false;
try {
gameThread.join();
} catch(InterruptedException e) {
Log.e("ERR", "Joining Thread");
}
}
public void resume() {
playing = true;
gameThread = new Thread(this);
gameThread.start();
}
To start the running man animation, we're going to wait the user click on the surface view. So, we need to override the onTouchEvent method and wait for an ACTION_DOWN event. When the event is made, we have just to change the isMoving boolean value. If man is running, we stop it. If man doesn't run, we start to move it :
Code:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN :
isMoving = !isMoving;
break;
}
return true;
}
Last thing to make is to assemble all the pieces of the puzzle, create the game view on the main activity, set it as the content view and then resume or pause the game animation when the activity is resumed or paused :
Code:
package com.ssaurel.runningman;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class RunningManAnimation extends AppCompatActivity {
private GameView gameView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gameView = new GameView(this);
setContentView(gameView);
}
@Override
protected void onResume() {
super.onResume();
gameView.resume();
}
@Override
protected void onPause() {
super.onPause();
gameView.pause();
}
class GameView extends SurfaceView implements Runnable {
private Thread gameThread;
private SurfaceHolder ourHolder;
private volatile boolean playing;
private Canvas canvas;
private Bitmap bitmapRunningMan;
private boolean isMoving;
private float runSpeedPerSecond = 500;
private float manXPos = 10, manYPos = 10;
private int frameWidth = 230, frameHeight = 274;
private int frameCount = 8;
private int currentFrame = 0;
private long fps;
private long timeThisFrame;
private long lastFrameChangeTime = 0;
private int frameLengthInMillisecond = 50;
private Rect frameToDraw = new Rect(0, 0, frameWidth, frameHeight);
private RectF whereToDraw = new RectF(manXPos, manYPos, manXPos + frameWidth, frameHeight);
public GameView(Context context) {
super(context);
ourHolder = getHolder();
bitmapRunningMan = BitmapFactory.decodeResource(getResources(), R.drawable.running_man);
bitmapRunningMan = Bitmap.createScaledBitmap(bitmapRunningMan, frameWidth * frameCount, frameHeight, false);
}
@Override
public void run() {
while (playing) {
long startFrameTime = System.currentTimeMillis();
update();
draw();
timeThisFrame = System.currentTimeMillis() - startFrameTime;
if (timeThisFrame >= 1) {
fps = 1000 / timeThisFrame;
}
}
}
public void update() {
if (isMoving) {
manXPos = manXPos + runSpeedPerSecond / fps;
if (manXPos > getWidth()) {
manYPos += (int) frameHeight;
manXPos = 10;
}
if (manYPos + frameHeight > getHeight()) {
manYPos = 10;
}
}
}
public void manageCurrentFrame() {
long time = System.currentTimeMillis();
if (isMoving) {
if (time > lastFrameChangeTime + frameLengthInMillisecond) {
lastFrameChangeTime = time;
currentFrame++;
if (currentFrame >= frameCount) {
currentFrame = 0;
}
}
}
frameToDraw.left = currentFrame * frameWidth;
frameToDraw.right = frameToDraw.left + frameWidth;
}
public void draw() {
if (ourHolder.getSurface().isValid()) {
canvas = ourHolder.lockCanvas();
canvas.drawColor(Color.WHITE);
whereToDraw.set((int) manXPos, (int) manYPos, (int) manXPos + frameWidth, (int) manYPos + frameHeight);
manageCurrentFrame();
canvas.drawBitmap(bitmapRunningMan, frameToDraw, whereToDraw, null);
ourHolder.unlockCanvasAndPost(canvas);
}
}
public void pause() {
playing = false;
try {
gameThread.join();
} catch(InterruptedException e) {
Log.e("ERR", "Joining Thread");
}
}
public void resume() {
playing = true;
gameThread = new Thread(this);
gameThread.start();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN :
isMoving = !isMoving;
break;
}
return true;
}
}
}
Now, you have just to run the application on your Android emulator or on your real device and to enjoy your Running Man Game Animation.
Don't hesitate to try this code and give me your feedbacks.
Thanks.
Sylvain
I just started learning self taught Android development and hopefully, I get to this level one days. Thanks for sharing your steps for this
Sent from my Nexus 6P using Tapatalk
nighthawk626 said:
I just started learning self taught Android development and hopefully, I get to this level one days. Thanks for sharing your steps for this
Sent from my Nexus 6P using Tapatalk
Click to expand...
Click to collapse
Great . I plan to make some tutorials for beginners too. Do you have some suggestions for that kind of tutorials ?
No suggestions, I'm just looking for a good place to begin. I first started with Android development then I scaled back to learn about Java, and still no luck, so I just started off with c++. Lol
Sent from my Nexus 6P using Tapatalk
Related
I'm developing an application that utilizes zooming in and out (for readability) and allows for swiping left and right to change pages.
Basically the flipping back and forth part works and zoom works. However, if I do any kind of zooming and then try to change a page the program crashes.
I've only tested this on the Epic 4G and on the G2 with the same problem.
Any help that can tell me what is going on or what I need to change would be greatly appreciated. (I tried to code out on the net to fix the HTC incredible having this kind of issue but the problem is still there)
Code:
package my.package;
import java.io.IOException;
import java.io.InputStreamReader;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SubMenu;
import android.view.View;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.View.OnTouchListener;
import android.webkit.WebView;
public class LessonShower extends Activity {
private char[] buffer = new char[128];
private WebView webView;
private int lessonNumber, lessonPage;
private AssetManager assetManager;
private int lessonsCount;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int lessonNumber = getIntent().getIntExtra("lessonNumber", 1);
int lessonPage = getIntent().getIntExtra("lessonPage", 1);
webView = new MyWebView(this);
assetManager = getAssets();
try {
lessonsCount = assetManager.list("").length - 3;
loadPage(lessonNumber, lessonPage);
} catch (IOException e) {
}
SimpleOnGestureListener gestureListener = new SimpleOnGestureListener(){
private static final int SWIPE_MIN_DISTANCE = 120;
private static final int SWIPE_THRESHOLD_VELOCITY = 200;
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
Log.d("debugging", "onFling");
if(Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY && e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE){
loadNextPage();
return true;
}
else if(e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE){
loadPrevPage();
return true;
}
if(e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE){
webView.pageDown(false);
}
else if(e2.getY() - e1.getY() > SWIPE_MIN_DISTANCE){
webView.pageUp(false);
}
return super.onFling(e1, e2, velocityX, velocityY);
}
};
final GestureDetector gDetector = new GestureDetector(webView.getContext(), gestureListener);
webView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View arg0, MotionEvent arg1) {
return gDetector.onTouchEvent(arg1);
}
});
webView.getSettings().setBuiltInZoomControls(true);
setContentView(webView);
}
public void loadNextPage(){
int nextPage = lessonPage + 1;
try{
InputStreamReader is = new InputStreamReader(assetManager.open("lesson" + lessonNumber + "/" + nextPage + ".html"));
readPage(is);
lessonPage++;
}
catch(IOException ex){
final CharSequence[] items = {"Home", "Next lesson"};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Choose an action");
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if(item == 0){
finish();
}
else{
if(lessonNumber < lessonsCount){
lessonNumber++;
lessonPage = 1;
loadPage(lessonNumber, lessonPage);
}
}
}
});
AlertDialog alert = builder.create();
alert.show();
}
}
public void loadPrevPage(){
if(lessonPage > 1){
try{
InputStreamReader is = new InputStreamReader(assetManager.open("lesson" + lessonNumber + "/" + --lessonPage + ".html"));
readPage(is);
}
catch(IOException ex){}
}
}
private void readPage(InputStreamReader is){
StringBuilder webPage = new StringBuilder();
int len = 0;
try {
while((len = is.read(buffer)) > 0){
webPage.append(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
webView.loadData(webPage.toString(), "", "");
}
public void loadPage(int lessonNumber, int lessonPage){
if(lessonNumber > lessonsCount){
return;
}
this.lessonNumber = lessonNumber;
this.lessonPage = lessonPage;
setTitle("Lesson" + lessonNumber);
InputStreamReader is = null;
try {
is = new InputStreamReader(assetManager.open("lesson" + lessonNumber + "/" + lessonPage + ".html"));
} catch (IOException e) {
}
readPage(is);
}
private static final int homeMenuItem = 1;
private static final int exitMenuItem = 2;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(Menu.NONE, homeMenuItem, Menu.NONE, "Home");
SubMenu pagesMenu = menu.addSubMenu("Pages");
try {
int lessonPages = assetManager.list("lesson" + lessonNumber).length;
for(int i = 1; i <= lessonPages; i++)
//"+ 1000" is used to escape collision with other menu items
pagesMenu.add(Menu.NONE, i + 1000, Menu.NONE, "Page" + i);
} catch (IOException e) {}
finally{
menu.add(Menu.NONE, exitMenuItem, Menu.NONE, "Hide");
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch(item.getItemId()){
case homeMenuItem:
finish();
break;
case exitMenuItem:
moveTaskToBack(true);
break;
default:
//Page was selected
if(item.getItemId() > 1000){
lessonPage = item.getItemId() - 1000;
loadPage(lessonNumber, lessonPage);
}
}
return true;
}
}
class MyWebView extends WebView{
private LessonShower lp = null;
public MyWebView(Context context) {
super(context);
lp = (LessonShower) context;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event){
if(keyCode == KeyEvent.KEYCODE_DPAD_LEFT){
lp.loadPrevPage();
}
else if(keyCode == KeyEvent.KEYCODE_DPAD_RIGHT){
lp.loadNextPage();
}
return false;
}
}
No ideas? Anyone?
Sent from my SPH-D700 using XDA App
I recently posted a problem I found out about having a GLSurfaceView on top of a Camera-Preview. The problem is, even though all older models are able to show this correctly, the Galaxy S3 seems to have a problem with that, giving wrong colors for transparent pixel. So since I thought I made a mistake with my shaders I made a new test project in OpenGL1.0. This is my code - a slightly changed Version of the Nehe Port for Android. But it still makes "over saturated" colors.
Code:
public class MainActivity extends Activity {
private GLSurfaceView mGLView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(this);
mGLView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
mGLView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
mGLView.setZOrderOnTop(true);
setContentView(new CameraView(this), new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mGLView.setRenderer(new Renderer2());
addContentView(mGLView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
}
@Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}
@Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}
}
Code:
public class CameraView extends SurfaceView implements SurfaceHolder.Callback{
SurfaceHolder surfaceHolder;
Camera camera;
public CameraView(Context context, AttributeSet attrs) {
super(context, attrs);
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
}
public CameraView(Context context) {
super(context);
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
try {
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(w, h);
camera.setParameters(parameters);
} catch (Exception e) {
Log.w("CameraView", "Exception:" , e);
}
camera.startPreview();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
camera = Camera.open();
try {
camera.setPreviewDisplay(holder);
} catch (IOException exception) {
camera.release();
camera = null;
}
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
camera.stopPreview();
camera.release();
camera = null;
}
}
Code:
public class Renderer2 implements Renderer {
private Triangle triangle;
public Renderer2() {
triangle = new Triangle();
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl.glClearDepthf(1.0f);
gl.glEnable(GL10.GL_DEPTH_TEST);
}
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
gl.glTranslatef(0.0f, 0.0f, -3.0f);
triangle.draw(gl);
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
if(height == 0) {
height = 1;
}
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 45.0f, (float)width / (float)height, 0.1f, 100.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
}
Code:
public class Triangle {
private FloatBuffer vertexBuffer;
private FloatBuffer colorBuffer;
private float vertices[] = { 0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f};
private float colors[] = { 1.0f, 0.0f, 0.0f, 0.5f,
0.0f, 1.0f, 0.0f, 0.5f,
0.0f, 0.0f, 1.0f, 0.5f};
public Triangle() {
ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuf.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
byteBuf = ByteBuffer.allocateDirect(colors.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
colorBuffer = byteBuf.asFloatBuffer();
colorBuffer.put(colors);
colorBuffer.position(0);
}
public void draw(GL10 gl) {
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vertices.length / 3);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
}
}
Please help me. Is there anyone who ever made an "augmented reality" app with transparency that works on a modern device (hopefully the S3) I'm thankful for any kind of help!
Tobias
take a look
I make an application for Android and I'm using CursorTreeAdapter as ExpandableListView. Now I want to use search box for display the filtered ExpandableListView items. Like this:
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Here's the code what I've so far:
MainActivity.java:
Code:
package com.example.android.exlistexample;
import java.util.HashMap;
import android.app.SearchManager;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.widget.ExpandableListView;
import android.widget.SearchView;
import android.widget.SearchView.OnCloseListener;
import android.widget.SearchView.OnQueryTextListener;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.example.android.exlistexample.provider.CfpContract;
public class MainActivity extends SherlockFragmentActivity {
private SearchView search;
private MyListAdapter listAdapter;
private ExpandableListView myList;
private final String DEBUG_TAG = getClass().getSimpleName().toString();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
search = (SearchView) findViewById(R.id.search);
search.setSearchableInfo(searchManager
.getSearchableInfo(getComponentName()));
search.setIconifiedByDefault(false);
search.setOnQueryTextListener(new OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
listAdapter.filterData(query);
expandAll();
return false;
}
@Override
public boolean onQueryTextChange(String query) {
listAdapter.filterData(query);
expandAll();
return false;
}
});
search.setOnCloseListener(new OnCloseListener() {
@Override
public boolean onClose() {
listAdapter.filterData("");
expandAll();
return false;
}
});
// get reference to the ExpandableListView
myList = (ExpandableListView) findViewById(R.id.expandableList);
// create the adapter
listAdapter = new MyListAdapter(null, MainActivity.this);
// attach the adapter to the list
myList.setAdapter(listAdapter);
Loader<Cursor> loader = getSupportLoaderManager().getLoader(-1);
if (loader != null && !loader.isReset()) {
runOnUiThread(new Runnable() {
public void run() {
getSupportLoaderManager().restartLoader(-1, null,
mSpeakersLoaderCallback);
}
});
} else {
runOnUiThread(new Runnable() {
public void run() {
getSupportLoaderManager().initLoader(-1, null,
mSpeakersLoaderCallback).forceLoad();
;
}
});
}
}
@Override
public void onResume() {
super.onResume();
getApplicationContext().getContentResolver().registerContentObserver(
CfpContract.Groups.CONTENT_URI, true, mSpeakerChangesObserver);
}
@Override
public void onPause() {
super.onPause();
getApplicationContext().getContentResolver().unregisterContentObserver(
mSpeakerChangesObserver);
}
// method to expand all groups
private void expandAll() {
int count = listAdapter.getGroupCount();
for (int i = 0; i < count; i++) {
myList.expandGroup(i);
}
}
public LoaderManager.LoaderCallbacks<Cursor> mSpeakersLoaderCallback = new LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.d(DEBUG_TAG, "onCreateLoader for loader_id " + id);
CursorLoader cl = null;
if (id != -1) {
if (id == 7) {
cl = new CursorLoader(getApplicationContext(),
CfpContract.Classes.CONTENT_URI,
MyListAdapter.CLASSES_PROJECTION, null, null,
CfpContract.Classes.DEFAULT_SORT);
} else if (id == 8) {
cl = new CursorLoader(getApplicationContext(),
CfpContract.Teachers.CONTENT_URI,
MyListAdapter.TEACHERS_PROJECTION, null, null,
CfpContract.Teachers.DEFAULT_SORT);
}
} else {
// group cursor
cl = new CursorLoader(getApplicationContext(),
CfpContract.Groups.CONTENT_URI,
MyListAdapter.GROUPS_PROJECTION, null, null,
CfpContract.Groups.DEFAULT_SORT);
}
return cl;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in.
int id = loader.getId();
Log.d("Dump Cursor MainActivity",
DatabaseUtils.dumpCursorToString(data));
Log.d(DEBUG_TAG, "onLoadFinished() for loader_id " + id);
if (id != -1) {
// child cursor
if (!data.isClosed()) {
Log.d(DEBUG_TAG, "data.getCount() " + data.getCount());
HashMap<Integer, Integer> groupMap = listAdapter
.getGroupMap();
try {
int groupPos = groupMap.get(id);
Log.d(DEBUG_TAG, "onLoadFinished() for groupPos "
+ groupPos);
listAdapter.setChildrenCursor(groupPos, data);
} catch (NullPointerException e) {
Log.w("DEBUG",
"Adapter expired, try again on the next query: "
+ e.getMessage());
}
}
} else {
listAdapter.setGroupCursor(data);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// is about to be closed.
int id = loader.getId();
Log.d(DEBUG_TAG, "onLoaderReset() for loader_id " + id);
if (id != 1) {
// child cursor
try {
listAdapter.setChildrenCursor(id, null);
} catch (NullPointerException e) {
Log.w(DEBUG_TAG,
"Adapter expired, try again on the next query: "
+ e.getMessage());
}
} else {
listAdapter.setGroupCursor(null);
}
}
};
private ContentObserver mSpeakerChangesObserver = new ContentObserver(
new Handler()) {
@Override
public void onChange(boolean selfChange) {
if (getApplicationContext() != null) {
runOnUiThread(new Runnable() {
public void run() {
getSupportLoaderManager().restartLoader(-1, null,
mSpeakersLoaderCallback);
}
});
}
}
};
}
MyListAdapter.java:
Code:
package com.example.android.exlistexample;
import java.util.HashMap;
import android.content.Context;
import android.database.Cursor;
import android.provider.BaseColumns;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorTreeAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.android.exlistexample.provider.CfpContract;
import com.example.android.exlistexample.provider.CfpContract.Groups;
import com.joanzapata.android.iconify.Iconify;
import com.joanzapata.android.iconify.Iconify.IconValue;
public class MyListAdapter extends CursorTreeAdapter {
public HashMap<String, View> childView = new HashMap<String, View>();
/**
* The columns we are interested in from the database
*/
protected static final String[] GROUPS_PROJECTION = new String[] {
BaseColumns._ID, CfpContract.Groups.GROUP_ID,
CfpContract.Groups.GROUP_NAME, CfpContract.Groups.GROUP_IMAGE, };
protected static final String[] CLASSES_PROJECTION = new String[] {
BaseColumns._ID, CfpContract.Classes.CLASS_ID,
CfpContract.Classes.CLASS_NAME, CfpContract.Classes.CLASS_CAT, };
protected static final String[] TEACHERS_PROJECTION = new String[] {
BaseColumns._ID, CfpContract.Teachers.TEACHER_ID,
CfpContract.Teachers.TEACHER_NAME,
CfpContract.Teachers.TEACHER_SHORT,
CfpContract.Teachers.TEACHER_OPLEIDINGEN, };
private final String DEBUG_TAG = getClass().getSimpleName().toString();
protected final HashMap<Integer, Integer> mGroupMap;
private MainActivity mActivity;
private LayoutInflater mInflater;
public MyListAdapter(Cursor cursor, Context context) {
super(cursor, context);
mActivity = (MainActivity) context;
mInflater = LayoutInflater.from(context);
mGroupMap = new HashMap<Integer, Integer>();
}
@Override
public View newGroupView(Context context, Cursor cursor,
boolean isExpanded, ViewGroup parent) {
final View view = mInflater.inflate(R.layout.list_group, parent, false);
return view;
}
@Override
public void bindGroupView(View view, Context context, Cursor cursor,
boolean isExpanded) {
TextView lblListHeader = (TextView) view
.findViewById(R.id.lblListHeader);
if (lblListHeader != null) {
lblListHeader.setText(cursor.getString(cursor
.getColumnIndex(Groups.GROUP_NAME)));
ImageView groupIcon = (ImageView) view
.findViewById(R.id.lblListHeaderIcon);
groupIcon.setImageResource(cursor.getInt(cursor
.getColumnIndex(Groups.GROUP_IMAGE)));
}
TextView IndicatorText = (TextView) view
.findViewById(R.id.lblListHeaderIndicator);
if (IndicatorText != null) {
if (isExpanded) {
Iconify.setIcon(IndicatorText, IconValue.icon_caret_up);
} else {
Iconify.setIcon(IndicatorText, IconValue.icon_caret_down);
}
}
}
@Override
public View newChildView(Context context, Cursor cursor,
boolean isLastChild, ViewGroup parent) {
final View view = mInflater.inflate(R.layout.list_item, parent, false);
return view;
}
@Override
public void bindChildView(View view, Context context, Cursor cursor,
boolean isLastChild) {
TextView txtListChild = (TextView) view.findViewById(R.id.lblListItem);
txtListChild.setText(cursor.getString(2));
}
protected Cursor getChildrenCursor(Cursor groupCursor) {
// Given the group, we return a cursor for all the children within that
// group
int groupPos = groupCursor.getPosition();
int groupId = groupCursor.getInt(groupCursor
.getColumnIndex(BaseColumns._ID));
Log.d(DEBUG_TAG, "getChildrenCursor() for groupPos " + groupPos);
Log.d(DEBUG_TAG, "getChildrenCursor() for groupId " + groupId);
mGroupMap.put(groupId, groupPos);
Loader loader = mActivity.getSupportLoaderManager().getLoader(groupId);
if (loader != null && !loader.isReset()) {
mActivity.getSupportLoaderManager().restartLoader(groupId, null,
mActivity.mSpeakersLoaderCallback);
} else {
mActivity.getSupportLoaderManager().initLoader(groupId, null,
mActivity.mSpeakersLoaderCallback);
}
return null;
}
// Access method
public HashMap<Integer, Integer> getGroupMap() {
return mGroupMap;
}
public void filterData(String query) {
// TODO Filter the data here
}
}
I have very considerably simplified and cleaned the code (so that you guys that not need to do).
As you can see, I've in total 3 cursors (1 for the groups and 2 for the children). The data is synced from a local database and put in a Content Provider. (The database is from here and the provider from here). From there I insert it into the CursorTreeAdapter. (The most of the loader functions is from here).
The only thing is now how do I implement a search? Should I do it trough Content Provider or a raw query in the database? I would like that the results of both children tables is displayed. I think because it's easy to make a fault while typing that `tokenize=porter` is a option in my case.
I've tried this in MyListAdapter.java (with FilterQueryProvider as someone suggested on Stack Overflow):
Code:
public void filterList(CharSequence constraint) {
final Cursor oldCursor = getCursor();
setFilterQueryProvider(filterQueryProvider);
getFilter().filter(constraint, new FilterListener() {
public void onFilterComplete(int count) {
// assuming your activity manages the Cursor
// (which is a recommended way)
notifyDataSetChanged();
// stopManagingCursor(oldCursor);
// final Cursor newCursor = getCursor();
// startManagingCursor(newCursor);
// // safely close the oldCursor
if (oldCursor != null && !oldCursor.isClosed()) {
oldCursor.close();
}
}
});
}
private FilterQueryProvider filterQueryProvider = new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
// assuming you have your custom DBHelper instance
// ready to execute the DB request
String s = '%' + constraint.toString() + '%';
return mActivity.getContentResolver().query(CfpContract.Classes.CONTENT_URI,
MyListAdapter.CLASSES_PROJECTION,
CfpContract.Classes.CLASS_NAME + " LIKE ? OR " + CfpContract.Classes.CLASS_CAT + " LIKE ?",
new String[] { s, s },
null);
}
};
And this in MainActivity.java:
Code:
search.setOnQueryTextListener(new OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
listAdapter.filterList(query);
expandAll();
return false;
}
@Override
public boolean onQueryTextChange(String query) {
listAdapter.filterList(query);
expandAll();
return false;
}
});
search.setOnCloseListener(new OnCloseListener() {
@Override
public boolean onClose() {
listAdapter.filterList("");
expandAll();
return false;
}
});
But then I get these errors when I try to search:
Code:
12-20 13:20:19.449: E/CursorWindow(28747): Failed to read row 0, column -1 from a CursorWindow which has 96 rows, 4 columns.
12-20 13:20:19.449: D/AndroidRuntime(28747): Shutting down VM
12-20 13:20:19.449: W/dalvikvm(28747): threadid=1: thread exiting with uncaught exception (group=0x415c62a0)
12-20 13:20:19.499: E/AndroidRuntime(28747): FATAL EXCEPTION: main
12-20 13:20:19.499: E/AndroidRuntime(28747): java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.
What I'm doing wrong? Or is this because I'm only return 1 query (Classes) instead of 2 (Classes and Teachers) in runQuery?
I hope that someone can point me in a good direction.
I am trying to start a new certain Activity based on which page I am clicking on in a Gridview.
I tried to understand the Sample GridViewPager which is coming along with the sdk and trying to adapt the given explanation on stackoverflow (question # 26343337). But I really don't know how to bring these two things together and even where to start.
The first java.file Selection
Code:
public class Selection extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.selection_grid);
final GridViewPager pager = (GridViewPager) findViewById(R.id.pager);
pager.setAdapter(new Workers(this, getFragmentManager()));
DotsPageIndicator dotsPageIndicator = (DotsPageIndicator) findViewById(R.id.page_indicator);
dotsPageIndicator.setPager(pager);
}
}
and the second java.file Users (thats the Adapter):
Code:
public class Users extends FragmentGridPagerAdapter {
private static final int TRANSITION_DURATION_MILLIS = 100;
private final Context mContext;
private List<Row> mRows;
private ColorDrawable mDefaultBg;
private ColorDrawable mClearBg;
public Users (Context ctx, FragmentManager fm) {
super(fm);
mContext = ctx;
mRows = new ArrayList<Workers.Row>();
mRows.add(new Row(cardFragment(R.string.title, R.string.user1)));
mRows.add(new Row(cardFragment(R.string.title, R.string.user2)));
mRows.add(new Row(cardFragment(R.string.title, R.string.user3)));
mRows.add(new Row(cardFragment(R.string.title, R.string.user4)));
// In case in one row several cardFragments are needed
// mRows.add(new Row(
// cardFragment(R.string.cards_title, R.string.cards_text),
// cardFragment(R.string.expansion_title, R.string.expansion_text)));
mDefaultBg = new ColorDrawable(R.color.dark_grey);
mClearBg = new ColorDrawable(android.R.color.transparent);
}
LruCache<Integer, Drawable> mRowBackgrounds = new LruCache<Integer, Drawable>(3) {
@Override
protected Drawable create(final Integer row) {
int resid = BG_IMAGES[row % BG_IMAGES.length];
new DrawableLoadingTask(mContext) {
@Override
protected void onPostExecute(Drawable result) {
TransitionDrawable background = new TransitionDrawable(new Drawable[] {
mDefaultBg,
result
});
mRowBackgrounds.put(row, background);
notifyRowBackgroundChanged(row);
background.startTransition(TRANSITION_DURATION_MILLIS);
}
}.execute(resid);
return mDefaultBg;
}
};
private Fragment cardFragment(int titleRes, int textRes) {
Resources res = mContext.getResources();
CardFragment fragment =
CardFragment.create(res.getText(titleRes), res.getText(textRes));
// Add some extra bottom margin to leave room for the page indicator
fragment.setCardMarginBottom(
res.getDimensionPixelSize(R.dimen.card_margin_bottom));
return fragment;
}
static final int[] BG_IMAGES = new int[] {
R.drawable.user1,
R.drawable.user2,
R.drawable.user3,
R.drawable.user4
};
/** A convenient container for a row of fragments. */
private class Row {
final List<Fragment> columns = new ArrayList<Fragment>();
public Row(Fragment... fragments) {
for (Fragment f : fragments) {
add(f);
}
}
public void add(Fragment f) {
columns.add(f);
}
Fragment getColumn(int i) {
return columns.get(i);
}
public int getColumnCount() {
return columns.size();
}
}
@Override
public Fragment getFragment(int row, int col) {
Row adapterRow = mRows.get(row);
return adapterRow.getColumn(col);
}
@Override
public Drawable getBackgroundForRow(final int row) {
return mRowBackgrounds.get(row);
}
@Override
public int getRowCount() {
return mRows.size();
}
@Override
public int getColumnCount(int rowNum) {
return mRows.get(rowNum).getColumnCount();
}
class DrawableLoadingTask extends AsyncTask<Integer, Void, Drawable> {
private static final String TAG = "Loader";
private Context context;
DrawableLoadingTask(Context context) {
this.context = context;
}
@Override
protected Drawable doInBackground(Integer... params) {
Log.d(TAG, "Loading asset 0x" + Integer.toHexString(params[0]));
return context.getResources().getDrawable(params[0]);
}
}
}
For my App I want when you click into the autocompletetextview it will bring up a list of chemicals and then when you choose your chemical it will put the chemical name in the autocomplete field and also put the corresponding weight into an edittext field.
The approach I went with this was using a customadapter so when you pick one it will give the other. It is my first time making one so I'm not sure if it's 100% correct. It would be great if you guys had any suggestions. Thanks
Currently this is the code that I have.
editText4 is the autocompletetextview field I want the chemical name to appear in.
editText is the editText field i want the number to appear in.
MainActivity .java
Code:
public void Chem() {
Chemical chemical_data[] = new Chemical[]
{
new Chemical("Acid", 125.33),
new Chemical("Blue", 145.3356),
};
ChemicalAdapter adapter = new ChemicalAdapter(this, R.layout.sollayout, chemical_data);
chemname = (AutoCompleteTextView) findViewById(R.id.editText4);
weightval = (EditText) findViewById(R.id.editText);
//ChemicalAdapter header = new ChemicalAdapter(this, R.layout.sollayout, null);
//weightval.addHeaderView(header);
chemname.setAdapter(adapter);
}
public void Chemname()
{
chemname = (AutoCompleteTextView) findViewById(R.id.editText4);
AutoCompleteTextView b = (AutoCompleteTextView) findViewById(R.id.editText4);
b.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Chem();
}
});
}
Chemical.java
Code:
public class Chemical {
String chemicalValue;
double weight;
public Chemical(String chemicalValue, double weight) {
this.chemicalValue = chemicalValue;
this.weight = weight;
}
public String getChemicalValue() {
return chemicalValue;
}
public double getWeight() {
return weight;
}
}
ChemicalAdapter.java
Code:
public class ChemicalAdapter extends ArrayAdapter<Chemical> {
Context context;
int layoutResourceId;
Chemical data[] = null;
public ChemicalAdapter(Context context, int layoutResourceId, Chemical[] data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ChemicalHolder holder = null;
if(row == null)
{
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new ChemicalHolder();
holder.chemname = (AutoCompleteTextView)row.findViewById(R.id.editText4);
holder.weightval = (EditText)row.findViewById(R.id.editText);
row.setTag(holder);
}
else
{
holder = (ChemicalHolder)row.getTag();
}
Chemical chemical = data[position];
holder.weightval.setText(String.valueOf(chemical.weight));
holder.chemname.setText(chemical.chemicalValue);
return row;
}
static class ChemicalHolder
{
AutoCompleteTextView chemname;
EditText weightval;
}
}