English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Creo que todos hemos jugado a juegos de rompecabezas de niños, ahora, con la difusión de los teléfonos móviles, hay cada vez más juegos que se pueden jugar en el teléfono, por lo que, recordando los buenos tiempos de la infancia, escribí este juego de rompecabezas simple y también puedo profundizar en algunos conocimientos básicos de Android.
Siguiendo la regla, primero el efecto visual:
Aquí he mezclado muy poco la imagen para mostrar el efecto, se puede cambiar en el código.
Primero, hay una imagen predeterminada, que se puede usar para el rompecabezas, o también se puede elegir una imagen favorita para el rompecabezas, el proceso del rompecabezas registrará el número de pasos movidos, y cuando el juego se gana, se mostrará un mensaje de sonrisa, indicando que el juego se ganó y cuántos pasos se usaron.
ps: Aquellos interesados pueden seguir expandiendo aquí, por ejemplo, agregar opciones de dificultad al juego, como dividir la imagen en más小块.
La idea general: cortar la imagen grande en varios小块, registrar la información de cada小块 en un array, mostrar cada小块 en un GridLayout, y marcar un小块 como小块 vacío (el小块 vacío puede intercambiar con小块 adyacentes), agregar eventos de clic a cada小块 en el GridLayout y eventos de gestos en toda la pantalla, cada vez que se haga clic o se detecte un gesto, se verifica si el小块 puede moverse, y finalmente se muestra un mensaje de victoria cuando el juego se gana.
Sin más preámbulos, ahora paso a explicar paso a paso el proceso de implementación del juego de rompecabezas.
1.Clase relacionada con los小块.
Este es el item de las variables de cada小块, clase, utilizada para administrar la información de cada小块 al cortar la imagen grande en cada小块. Es muy simple, son variables y métodos Setter y Getter, aquí va el código~
/** * Created by yyh on 2016/10/21. */ public class GameItemView{ /** * La información de cada小块 */ //La posición x real de cada小块, private int x=0; //La posición y real de cada小块, private int y=0; //La imagen de cada小块, private Bitmap bm; //La posición x de la imagen de cada小块, private int p_x=0; //La posición y de la imagen de cada小块. private int p_y=0; public GameItemView(int x, int y, Bitmap bm) { super(); this.x = x; this.y = y; this.bm = bm; this.p_x=x; this.p_y=y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public Bitmap getBm() { return bm; } public void setBm(Bitmap bm) { this.bm = bm; } public int getP_x() { return p_x; } public void setP_x(int p_x) { this.p_x = p_x; } public int getP_y() { return p_y; } public void setP_y(int p_y) { this.p_y = p_y; } /** * juzgar si la posición de cada小块 es correcta * @return */ public boolean isTrue(){ if (x==p_x&&y==p_y){ return true; } return false; } }
2.la disposición de la interfaz principal
la interfaz principal es sencilla, un Botón para cambiar la imagen, un ImageView para mostrar la imagen original, un GridLayout para jugar el puzzle, y finalmente, un TextView para mostrar cuántos pasos se utilizaron para completar el puzzle. La disposición es la siguiente:
<?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="match_parent" > <LinearLayout android:id="@"+id/ll" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" > <Button android:id="@"+id/bt_choice" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="seleccionar imagen" android:adjustViewBounds="true" /> </LinearLayout> <ImageView android:id="@"+id/iv" android:layout_below="@id/ll" android:adjustViewBounds="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/haizei" android:layout_marginTop="3dp" ></ImageView> ¡<!-- la interfaz principal del juego--> <GridLayout android:layout_marginTop="3dp" android:layout_below="@id/iv" android:id="@"+id/gl" android:layout_width="wrap_content" android:layout_height="wrap_content" android:columnCount="5" android:rowCount="3" android:adjustViewBounds="true" > </GridLayout> <TextView android:id="@"+id/tv_step" android:layout_below="@id/gl" android:layout_marginTop="3dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="pasos utilizados: 0" android:textSize="26sp" /> </RelativeLayout>
3.abrir la selección de imágenes
darle un evento de clic al Botón, llamar al método startActivityForResult(Intent intent, int requestCode); para obtener una imagen.
bt_choice.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent= new Intent("android.intent.action.GET_CONTENT"); intent.setType("image", "el tipo de intento es "image",/*" startActivityForResult(intent, CHOICE_PHOTO);//abrir el álbum } });
Se debe sobrescribir el método onActivityResult(int requestCode, int resultCode, Intent data) en el Activity para mostrar la imagen seleccionada y inicializar el juego. (Después de seleccionar la imagen, se debe proceder al corte de la imagen y al inicio del juego de rompecabezas.)
protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode){ case CHOICE_PHOTO: if (resultCode == RESULT_OK){ //juzgar la versión del sistema del teléfono if (Build.VERSION.SDK_INT >=19{ handleImageOnKitKat(data); //obtener la imagen en el imageview BitmapDrawable bitmapDrawable = (BitmapDrawable) photo.getDrawable(); bt_tupan = bitmapDrawable.getBitmap(); //eliminación de los cuadrados pequeños del GridLayout original, removeGameItem(); //corte de la nueva imagen en cuadrados pequeños y su adición a GridLayout. setGameItem(); //comienzo del juego startGame(); }else { handleImageBeforeKitKat(data); //obtener la imagen en el imageview BitmapDrawable bitmapDrawable = (BitmapDrawable) photo.getDrawable(); bt_tupan = bitmapDrawable.getBitmap(); //eliminación de los cuadrados pequeños del GridLayout original, removeGameItem(); //corte de la nueva imagen en cuadrados pequeños y su adición a GridLayout. setGameItem(); //comienzo del juego startGame(); } } } }
Luego viene la implementación de la función específica para seleccionar la imagen. Las notas son muy claras, sin necesidad de más explicaciones. Nuestro enfoque está en la implementación específica de la rompecabezas y los cambios de gestos, aquí hay muchas formas de seleccionar la imagen. No se detendrá en esto, hay marcos disponibles en línea.
//el teléfono no es mayor que19método de obtener datos private void handleImageBeforeKitKat(Intent data) { Uri uri = data.getData(); String imagePath = getImagePath(uri, null); displayImage(imagePath); } /** * el teléfono es mayor que19método de obtener datos * @param data */ @TargetApi(Build.VERSION_CODES.KITKAT) private void handleImageOnKitKat(Intent data) {}} String imagePath=null; Uri uri=data.getData(); if (DocumentsContract.isDocumentUri(this,uri)){ //Si la url es del tipo document, se procesa mediante el id del document. String docId=DocumentsContract.getDocumentId(uri); if ("com.android.providers.media.documents".equals(uri.getAuthority())){ String id =docId.split(":")[1];//Extraer el id en formato numérico; String selection= MediaStore.Images.Media._ID+"="+id; imagePath=getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection); } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())){ Uri contenturi= ContentUris.withAppendedId(Uri.parse("content:"),//descargas/public_downloads", Long.valueOf(docId)); imagePath=getImagePath(contenturi,null); } } else if ("content".equalsIgnoreCase(uri.getScheme())){ //Si el uri no es del tipo document, se procesa de manera común. imagePath=getImagePath(uri,null); } displayImage(imagePath); } /** * Mostrar imagen * @param imagePath //La ruta de la imagen. */ private void displayImage(String imagePath) { if (imagePath != null) { Bitmap bitmap = BitmapFactory.decodeFile(imagePath); if (isHeigthBigWidth(bitmap)) { Bitmap bt = rotaingImageView(bitmap);//girar la imagen90 grados. Bitmap disbitmapt = ajustBitmap(bt); photo.setImageBitmap(disbitmapt); } else { Bitmap disbitmap = ajustBitmap(bitmap); photo.setImageBitmap(disbitmap); } } } /** * ajustar la orientación de la imagen * @param bitmap * @return */ private Bitmap rotaingImageView(Bitmap bitmap) { //acción de girar la imagen Matrix matrix = new Matrix();; matrix.postRotate(270); // crear nueva imagen Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); return resizedBitmap; } /** * obtener la ruta de la imagen * @param externalContentUri * @param selection * @return */ private String getImagePath(Uri externalContentUri, String selection) { String path = null; Cursor cursor = getContentResolver().query(externalContentUri, null, selection, null, null); if (cursor != null) { if (cursor.moveToFirst()) { path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } } cursor.close(); return path; }
4.Proceso de formación de cada小块 del rompecabezas.}}
Observando cada小块, usar GridLayout es lo más conveniente. Por lo tanto, usar un GridLayout para mostrar cada小块 cortado de la imagen grande, usar un array de ImageView para guardar la información de cada小块 y, por defecto, establecer el último小块 como un小块 vacío.
Primero son las variables necesarias. Los comentarios son muy claros.
/** * Crear varios cuadros de juego usando un array bidimensional */ private ImageView [][] iv_game_arr=new ImageView[3][5]; /** *Interfaz principal del juego * */ private GridLayout gl_game_layout; //Filas y columnas de小块 private int i; private int j; /**Variable global de小块 vacío*/ private ImageView iv_null_imagview;
Luego es obtener la imagen desde Imageview y cortar la imagen en filas y columnas según ciertos patrones (aquí se configura el rompecabezas como3filas5En la columna). Guardar la información de cada小块 cortado en un array de ImageView. Establecer Tag y escucha de clic para cada小块.
private void setGameItem() { //Ajustar el tamaño de la imagen Bitmap abitmap=ajustBitmap(bt_tupan); int ivWidth=getWindowManager().getDefaultDisplay().getWidth()/5;//Ancho y alto de cada小块 de juego. Cortar en cuadrados int tuWidth=abitmap.getWidth()/5; for (int i=0;i<iv_game_arr.length;i++{ for (int j=0;j<iv_game_arr[0].length;j++{ //Cortar la imagen grande en小块 Bitmap bm=Bitmap.createBitmap(abitmap,j*tuWidth,i*tuWidth,tuWidth,tuWidth); iv_game_arr[i][j]=new ImageView(this); iv_game_arr[i][j].setImageBitmap(bm);//Establecer el patrón de cada小块 iv_game_arr[i][j].setLayoutParams(new RelativeLayout.LayoutParams(ivWidth, ivWidth)); //Establecer el espacio entre los cuadrados iv_game_arr[i][j].setPadding(2, 2, 2, 2); iv_game_arr[i][j].setTag(new GameItemView(i, j, bm)); //Asociar datos personalizados iv_game_arr[i][j].setOnClickListener(new View.OnClickListener() { ...... );
Por supuesto, las imágenes que elegimos no pueden ser todas del tamaño estándar, por lo que, antes de cortar la imagen, debemos ajustarla. Ajustar la imagen a5:3proporción. (de esta manera, cortado en3filas5sólo los cuadrados pequeños de la columna pueden cortarse correctamente) aquí, calculo previamente el espacio entre cada cuadrado pequeño en width.
//Ajustar el tamaño de la imagen private Bitmap ajustBitmap(Bitmap bitmap) { int width=getWindowManager().getDefaultDisplay().getWidth()-(iv_game_arr[0].length-1)*2; int heigth=width/5*3; Bitmap scaledBitmap=Bitmap.createScaledBitmap(bitmap, width, heigth, true); return scaledBitmap; }
Insertar cada cuadrado pequeño en el GridLayout.
/** * Insertar el cuadrado pequeño en GridLayout */ private void startGame() { tv_step.setText("Pasos utilizados: 0"); for (i = 0; i <iv_game_arr.length; i++{ for (j = 0; j <iv_game_arr[0].length; j++{ gl_game_layout.addView(iv_game_arr[i][j]); } } //Establecer el último cuadrado como el cuadrado vacío configurado. setNullImageView(iv_game_arr[i-1-1]
5. Proceso de clic del cuadrado pequeño y detección de gestos
Este es el núcleo del juego de rompecabezas, entender la regla de cambio de movimiento de los cuadrados pequeños es entender el juego de rompecabezas.
Para el evento de clic, primero obtener toda la información del cuadrado seleccionado (posición, patrón) y la información de posición del cuadrado vacío, determinar si el cuadrado seleccionado está adyacente al cuadrado vacío. Si están adyacentes, realizar el intercambio de datos (usar TranslateAnimation para realizar el animación de movimiento). Si no están adyacentes, no se realiza ninguna acción.
a. Método para determinar si el cuadrado seleccionado está adyacente al cuadrado vacío
/** * Determinar si el cuadrado seleccionado está adyacente al cuadrado vacío. * @param imageView 当前点击的方块 * @return true: Adyacente. false: No adyacente. */ public boolean isAdjacentNullImageView(ImageView imageView){ //Obtener la posición actual del cuadrado vacío y la posición del cuadrado pulsado GameItemView null_gameItemView = (GameItemView) iv_null_imagview.getTag(); GameItemView now_gameItem_view = (GameItemView) imageView.getTag(); if(null_gameItemView.getY()==now_gameItem_view.getY()&&now_gameItem_view.getX()+1==null_gameItemView.getX()){//El bloque actual que se hace clic está encima del bloque vacío return true; }else if(null_gameItemView.getY()==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX()+1{//El bloque actual que se hace clic está debajo del bloque vacío return true; }else if(null_gameItemView.getY()==now_gameItem_view.getY()+1&&now_gameItem_view.getX()==null_gameItemView.getX()){//El bloque actual que se hace clic está a la izquierda del bloque vacío return true; }else if(null_gameItemView.getY()+1==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX(){ ////El bloque actual que se hace clic está a la derecha del bloque vacío return true; } return false; }
b. A continuación, si están adyacentes, se entra en el método de intercambio de datos del cuadrado
Aquí hay una sobrecarga de método, si se necesita un efecto de animación, el intercambio de datos sin animación se prepara para desordenar el rompecabezas al inicializar el juego. Aquí se enumera el código de intercambio central. Después de cada intercambio, también se debe determinar si el juego ha sido ganado (es decir, si el rompecabezas se ha completado ~).
//Obtener los datos asociados al cuadrado pulsado GameItemView gameItemView = (GameItemView) itemimageView.getTag(); //Establecer el patrón del cuadrado vacío como el cuadrado pulsado iv_null_imagview.setImageBitmap(gameItemView.getBm()); //Obtener los datos asociados al cuadrado vacío GameItemView null_gameItemView = (GameItemView) iv_null_imagview.getTag(); //Intercambiar datos (pasar los datos del bloque que se hace clic al bloque vacío) null_gameItemView.setBm(gameItemView.getBm()); null_gameItemView.setP_x(gameItemView.getP_x()); null_gameItemView.setP_y(gameItemView.getP_y()); //Establecer el bloque actual que se hace clic como bloque vacío. setNullImageView(itemimageView); if (isStart){ isGameWin();//Si tiene éxito, se mostrará una notificación emergente. }
c. Configuración de la animación durante el intercambio
Al intercambiar la configuración de la animación, primero determine la dirección de movimiento, configure diferentes animaciones de movimiento según la dirección, y luego escuche la finalización de la animación para realizar operaciones de intercambio de datos. Es decir, después de b. si están adyacentes, entre al método de intercambio de datos del bloque. Finalmente, ejecutar la animación.
//1.Crear una animación, configurar la dirección y la distancia de movimiento //Determinar la dirección, configurar la animación if (itemimageView.getX()>iv_null_imagview.getX()){//El bloque actual que se hace clic está encima del bloque vacío //mover hacia abajo translateAnimation = new TranslateAnimation(0.1f,-itemimageView.getWidth(),0.1f,0.1f); }else if (itemimageView.getX()<iv_null_imagview.getX()){//El bloque actual que se hace clic está debajo del bloque vacío //mover hacia arriba boolean f=itemimageView.getX()<iv_null_imagview.getX(); //Log.i("Click en el bloque","sssssssssssssssssssssssss"+f); translateAnimation = new TranslateAnimation(0.1f,itemimageView.getWidth(),0.1f,0.1f); }else if (itemimageView.getY()>iv_null_imagview.getY()){//El bloque actual que se hace clic está a la izquierda del bloque vacío //mover a la derecha translateAnimation=new TranslateAnimation(0.1f,0.1f,0.1f,-itemimageView.getWidth()); else if(itemimageView.getY()<iv_null_imagview.getY()){//El bloque actual que se hace clic está a la derecha del bloque vacío //Mover a la izquierda translateAnimation=new TranslateAnimation(0.1f,0.1f,0.1f,itemimageView.getWidth()); } //2.Establecer varios parámetros de la animación translateAnimation.setDuration(80); translateAnimation.setFillAfter(true); //3.Establecer el escucha de la animación translateAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { isAminMove=true; } @Override public void onAnimationEnd(Animation animation) { //Finalizar animación, intercambiar datos ...... } //Ejecutar animación itemimageView.startAnimation(translateAnimation);
El flujo del evento de clic se ha completado, ahora sigue el evento de detección de gestos. Es decir, no solo se puede mover el cuadrado pequeño mediante clic, sino también mediante gestos.
One. Crear objeto de gestos
En el método onFling completar las operaciones relacionadas con el gesto.
//Crear objeto de gestos gestureDetector =new GestureDetector(this, new GestureDetector.OnGestureListener() { @Override public boolean onDown(MotionEvent e) { return false; } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return false; } @Override public void onLongPress(MotionEvent e) { } @Override public boolean onFling(MotionEvent e1, MotionEvent e2float velocityX, float velocityY) { //Operaciones relacionadas con el gesto ...... }
A continuación, realizamos operaciones específicas en el método onFling
Two. Determinar la dirección del movimiento del gesto
Obtener diferentes direcciones de movimiento según los diferentes valores de retorno.
/** * Aumentar el deslizamiento del gesto, determinar si es deslizamiento de arriba-abajo o izquierda-derecha según el gesto * @param start_x Punto de inicio del gesto en x * @param start_y Punto de inicio del gesto en y * @param end_x Punto final del gesto en x * @param end_y Punto final del gesto en y * @return 1:Arriba 2:Abajo 3:Izquierda 4:Derecha */ public int getDirctionByGesure(float start_x,float start_y,float end_x,float end_y){ boolean isLeftOrRight =(Math.abs(end_x-start_x)>Math.abs(end_y-start_y))?true:false; //Si es Izquierda o Derecha if(isLeftOrRight){//Izquierda y Derecha boolean isLeft=(end_x-start_x)>0?false:true; if(isLeft){ return 3; }else { return 4; } } else {//Arriba y Abajo boolean isUp=(end_y-start_y)>0?false:true; if (isUp){ return 1; }else { return 2; } } }
Three. Determinar si se puede mover y realizar la operación de movimiento según el cuadrado vacío y la dirección del movimiento.
Como es un gesto, lo que se mueve tiene que ser los cuadrados alrededor del cuadrado vacío, por lo que el punto clave es determinar la dirección del cuadrado vacío en la dirección a mover, y luego determinar si se puede mover según la dirección, realizar la operación de movimiento. (El método changeDateByImageView() es la operación específica de intercambio de datos y movimiento de los cuadrados. Es el método del evento de clic.)
/**Anular el método changeByDirGes(int type); * Mover los cuadrados adyacentes al cuadrado vacío según la dirección del gesto. * @param type Valor de retorno de la dirección 1:Arriba 2:Abajo 3:Izquierda 5:Derecha * @param isAnim Si hay animación true: hay animación, false: sin animación */ public void changeByDirGes(int type,boolean isAnim){ //1Obtener la posición actual del cuadrado vacío. GameItemView null_gameItemView = (GameItemView) iv_null_imagview.getTag(); int new_x = null_gameItemView.getX(); int new_y = null_gameItemView.getY(); //2.Se debe establecer la coordenada del vecino correspondiente según la dirección. if (type==1{//Lo que significa que el bloque vacío está encima del bloque que se debe mover. new_x++; } else if (type==2{//El bloque vacío está debajo del bloque que se debe mover new_x--; } else if (type==3{//El bloque vacío está a la izquierda del bloque que se debe mover new_y++; } else if (type==4{//El bloque vacío está a la derecha del bloque que se debe mover new_y--; } //3.Se debe verificar si existe esta nueva coordenada if (new_x >= 0 && new_x < iv_game_arr.length && new_y >= 0 && new_y < iv_game_arr[0].length) { //Existe, se puede mover y intercambiar datos if (isAnim) {//Con animación changeDateByImageView(iv_game_arr[new_x][new_y]); } else { changeDateByImageView(iv_game_arr[new_x][new_y], isAnim); } } else { //No hacer nada } }
Bueno, el evento de gesto se ha completado con éxito~
Claro, aquí hay dos puntos a tener en cuenta.1.Primero, se debe establecer el método onTouchEvent() del Activity actual, para que el evento de toque sea procesado por gestos, y también se debe establecer el método dispatchTouchEvent(), dentro del cual también se debe distribuir el evento de gesto hacia abajo, de lo contrario, en el GridLayout, solo se puede desencadenar el evento de clic y el evento de gesto no tendrá efecto.2.Se debe agregar una bandera para indicar si se está moviendo, si se está moviendo, no se debe hacer nada, de lo contrario, cada vez que se haga clic en el pequeño bloque, incluso si se está moviendo, también se desencadenará el evento de clic, lo que causará el movimiento de animación nuevamente, lo que causará una mala experiencia de usuario.
@Override public boolean onTouchEvent(MotionEvent event) { return gestureDetector.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { gestureDetector.onTouchEvent(ev); return super.dispatchTouchEvent(ev); }
6.El juego comienza a desordenar los bloques y cuando termina, muestra un Toast de notificación.
El código es simple, aquí está el código, donde el Toast emergente es un Toast con animación de View personalizada.
//Desordena el orden de las imágenes aleatoriamente public void randomOrder(){ //Número de veces desordenadas, para facilitar la prueba, se configura muy pequeño. for (int i=0;i<5;i++{ //Intercambia datos según el gesto, sin animación. int type = (int) (Math.random()*4)+1; // Log.i("sssssssssfdfdfd", "Número de intercambios"+i+"Valor de type"+type); changeByDirGes(type, false); } } /** * Método para determinar si el juego ha terminado */ public void isGameWin(){ //Indicador de victoria en el juego boolean isGameWin =true; //Revisa cada pequeño cuadro for (i = 0; i <iv_game_arr.length; i++{ for (j = 0; j <iv_game_arr[0].length; j++{ //No se juzga el cuadro vacío, salta if (iv_game_arr[i][j]==iv_null_imagview){ continue; } GameItemView gameItemView= (GameItemView) iv_game_arr[i][j].getTag(); if (!gameItemView.isTrue()){ isGameWin=false; //Sal del bucle interno break; } } if (!isGameWin){ //Sal del bucle externo break; } } //Decide según una variable de interruptor si el juego ha terminado, y da una sugerencia cuando termina. if (isGameWin){ // Toast.makeText(this, "Has ganado el juego", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Felicidades, has ganado el juego, has utilizado "+step+"Paso", ToastUtil.LENGTH_SHORT, ToastUtil.SUCCESS); step=0; } }
Bueno, la parte importante ya está terminada, aquí también hay un Toast de View personalizada. La explicación detallada de Toast se abordará en el siguiente artículo, aquí se explica brevemente el proceso de implementación de Toast personalizado.
Primero, crea una clase SuccessToast (la sonrisa incluye el ojo izquierdo, el ojo derecho y la curva de sonrisa). Proporcionamos el proceso central. Utilizando animaciones, realizamos el proceso de dibujo dinámico de la sonrisa.
@Override protected void onDraw(Canvas canvas) {}} super.onDraw(canvas); mPaint.setStyle(Paint.Style.STROKE); //Dibujar el arco de la sonrisa canvas.drawArc(rectF, 180, endAngle, false, mPaint); mPaint.setStyle(Paint.Style.FILL); if (isSmileLeft) { //Si es el ojo izquierdo, dibujar el ojo izquierdo canvas.drawCircle(mPadding + mEyeWidth + mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint); } if (isSmileRight) { //Si tiene ojos, dibujar el ojo derecho. canvas.drawCircle(mWidth - mPadding - mEyeWidth - mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint); } } /** * Método para comenzar la animación * @param startF Valor inicial * @param endF Valor final * @param time Tiempo de la animación * @return */ private ValueAnimator startViewAnim(float startF, final float endF, long time) { //Configurar los valores de inicio y final de valueAnimator. valueAnimator = ValueAnimator.ofFloat(startF, endF); //Configurar el tiempo de la animación valueAnimator.setDuration(time); //Configurar el interpolador. Controlar la velocidad de cambio de la animación valueAnimator.setInterpolator(new LinearInterpolator()); //Configurar el escuchador. Escuchar los cambios en el valor de la animación y actuar en consecuencia. valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { mAnimatedValue = (float) valueAnimator.getAnimatedValue(); //si el valor de value es menor que 0.5 if (mAnimatedValue < 0.5) { isSmileLeft = false; isSmileRight = false; endAngle = -360 * (mAnimatedValue); //si el valor de value está en0.55y0.7entre } else if (mAnimatedValue > 0.55 && mAnimatedValue < 0.7) { endAngle = -180; isSmileLeft = true; isSmileRight = false; //otros } else { endAngle = -180; isSmileLeft = true; isSmileRight = true; } //redibujo postInvalidate(); } }); if (!valueAnimator.isRunning()) { valueAnimator.start(); } return valueAnimator; }
Luego, cree un archivo success_toast_layout.xml para completar el diseño del toast. El diseño es a izquierda y derecha (izquierda: vista de sonrisa, derecha: TextView de sugerencia).
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@"+id/root_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#00000000" android:orientation="vertical"> <LinearLayout android:id="@"+id/base_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="25dp" android:layout_marginLeft="30dp" android:layout_marginRight="30dp" android:layout_marginTop="25dp" android:background="@drawable"/background_toast" android:orientation="horizontal"> <LinearLayout android:id="@"+id/linearLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <com.example.yyh.puzzlepicture.activity.Util.SuccessToast android:id="@"+id/successView" android:layout_width="50dp" android:layout_height="50dp" android:layout_gravity="center_vertical|left" android:layout_margin="10px" android:gravity="center_vertical|left" /> </LinearLayout> <TextView android:id="@"+id/toastMessage" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:padding="10dp" android:text="New Text" /> </LinearLayout> </LinearLayout>
Finally, create a ToastUtil class to manage the custom Toast.
/** * Created by yyh on 2016/10/25. */ public class ToastUtil { public static final int LENGTH_SHORT = 0; public static final int LENGTH_LONG = 1; public static final int SUCCESS = 1; static SuccessToast successToastView; public static void makeText(Context context, String msg, int length, int type) { Toast toast = new Toast(context); switch (type) { case 1: { View layout = LayoutInflater.from(context).inflate(R.layout.success_toast_layout, null, false); TextView text = (TextView) layout.findViewById(R.id.toastMessage); text.setText(msg); successToastView = (SuccessToast) layout.findViewById(R.id.successView); successToastView.startAnim(); text.setBackgroundResource(R.drawable.success_toast); text.setTextColor(Color.parseColor("#FFFFFF")); toast.setView(layout); break; } } toast.setDuration(length); toast.show(); } }
De esta manera, se puede llamar a este Toast personalizado en ManiActivity.
Bueno, final.
Código fuente del juego:Juego de rompecabezasel proceso de implementación
gitHub:Juego de rompecabezas.
Esto es todo el contenido del artículo, espero que sea útil para su aprendizaje y que todos nos apoyen en el tutorial de clamor.
Aclaración: Este artículo se comparte en línea, el derecho de autor pertenece al propietario original, el contenido se carga de manera autónoma por los usuarios de Internet, este sitio web no posee los derechos de propiedad, no se ha procesado editorialmente y no asume ninguna responsabilidad legal relacionada. Si encuentra contenido sospechoso de infracción de derechos de autor, por favor envíe un correo electrónico a: notice#w3Declaración: El contenido de este artículo se obtiene de la red, el derecho de autor pertenece al propietario original, el contenido se contribuye y carga de manera autónoma por los usuarios de Internet, este sitio web no posee los derechos de propiedad, no se ha procesado editorialmente y no asume ninguna responsabilidad legal relacionada. Si encuentra contenido sospechoso de infracción de derechos de autor, por favor envíe un correo electrónico a: notice#w