English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Вероятно, все мы играли в пазл в детстве. В наши дни, с распространением мобильных телефонов, игры на телефонах становятся все более разнообразными, поэтому я решил重温童年的记忆,написав эту простую игру для пазла, и также можно углубить свои знания о базовых знаниях Android.
По старой памяти, сперва效果图:
Здесь я разложил изображения для демонстрации эффекта, очень редко, в коде можно изменить.
Сначала, у нас естьDefault изображение, которое можно использовать для пазла, или вы можете выбрать любое изображение, которое вам нравится, для пазла. Процесс пазла будет записывать количество шагов, и когда игра выиграна, появится提示笑脸, игра выиграна, сколько шагов было использовано.
ps: Если вас это интересует, вы完全可以在此基础上进行扩展, например, добавить опции для повышения сложности игры, такие как разделение изображений на более мелкие方块 и т.д.
Основная идея: разрезать большое изображение на отдельные小块, записать информацию о каждом小块е в массив, показать каждый小块 с помощью GridLayout, и отметить один小块 как пустой小块 (пустой小块 может обмениваться с相邻ними小块ами), добавить события нажатия на каждый小块 и события жеста на всего экране, каждый раз, когда нажимается или выполняется жест, проверять, можно ли передвигать小块, и в конце игры показать уведомление о победе.
Без weiterer Worte, дальше идет поэтапное реализация процесса пазла.
1. Класс, связанный с小块ом.
Это различные переменные, класс, используемый для управления информацией о каждом小块е, полученном из большого изображения, разбитого на каждый小块. Очень просто, это различные переменные и методы setter и getter, просто код ~
/** * Создано yyh 21.10.2016. */ public class GameItemView{ /** * Информация о каждом小块е */ //每个小方块的实际位置x, private int x=0; //每个小方块的实际位置y, private int y=0; //每个小方块的图片, private Bitmap bm; //每个小方块的图片位置x, private int p_x=0; //每个小方块的图片位置y. 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; } /** * Определить правильное положение каждого小块 * @return */ public boolean isTrue(){ if (x==p_x&&y==p_y){ return true; } return false; } }
2. Расположение главного экрана
Интерфейс главного экрана прост: кнопка для замены изображения, ImageView для отображения исходного изображения, GridLayout для игры в пазл, и, наконец, TextView для отображения количества шагов, необходимых для завершения пазла. Расположение следующим образом:
<?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="Выберите изображение" 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> <!-- Игровой интерфейс --> <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="已用步数:0" android:textSize="26sp" /> </RelativeLayout>
3. Откройте изображение и выберите изображение
Установить событие нажатия на кнопку, вызвать метод startActivityForResult(Intent intent, int requestCode); для получения изображения.
bt_choice.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent, CHOICE_PHOTO); // Открываем галерею } });
В Activity перепишите метод onActivityResult(int requestCode, int resultCode, Intent data) для отображения выбранного изображения и инициализации игры. (После выбора изображения, необходимо его разрезать и начать пазл.)
protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case CHOICE_PHOTO: if (resultCode == RESULT_OK) { //判断手机系统版本 if (Build.VERSION.SDK_INT >= 19) { handleImageOnKitKat(data); //得到imageview中的图片 BitmapDrawable bitmapDrawable = (BitmapDrawable) photo.getDrawable(); bt_tupan = bitmapDrawable.getBitmap(); //将原来GridLayout中的小方块移除, removeGameItem(); //将新图切割成小方块并加入GridLayout. setGameItem(); //开始游戏 startGame(); } else { handleImageBeforeKitKat(data); //得到imageview中的图片 BitmapDrawable bitmapDrawable = (BitmapDrawable) photo.getDrawable(); bt_tupan = bitmapDrawable.getBitmap(); //将原来GridLayout中的小方块移除, removeGameItem(); //将新图切割成小方块并加入GridLayout. setGameItem(); //开始游戏 startGame(); } } } }
Затем реализация конкретной функции выбора изображения. Комментарии очень ясны, не будем вдаваться в подробности. Наше внимание сосредоточено на конкретной реализации пазла и изменений жестов, здесь есть много способов выбора изображения. Не будем углубляться, в Интернете есть готовые фреймворки.
//手机不大于19的取数据方法 private void handleImageBeforeKitKat(Intent data) { Uri uri = data.getData(); String imagePath = getImagePath(uri, null); displayImage(imagePath); } /** * Метод получения данных для телефона с версией более 19 * @param data */ @TargetApi(Build.VERSION_CODES.KITKAT) private void handleImageOnKitKat(Intent data) { String imagePath = null; Uri uri = data.getData(); if (DocumentsContract.isDocumentUri(this, uri)){ //Если URL типа document, то обрабатывается через идентификатор document. String docId = DocumentsContract.getDocumentId(uri); if ("com.android.providers.media.documents".equals(uri.getAuthority())){ String id = docId.split(":")[1]; // Получается числовой формат id; 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://downloads/public_downloads"), Long.valueOf(docId)); imagePath = getImagePath(contenturi, null); } else if ("content".equalsIgnoreCase(uri.getScheme())){ //Если URI не типа document, то обрабатывается обычным способом. imagePath = getImagePath(uri, null); } displayImage(imagePath); } /** * Показывать изображение * @param imagePath // Путь к изображению. */ private void displayImage(String imagePath) { if (imagePath != null) { Bitmap bitmap = BitmapFactory.decodeFile(imagePath); if (isHeigthBigWidth(bitmap)) { Bitmap bt = rotaingImageView(bitmap); // Поворот изображения на 90 градусов. Bitmap disbitmapt = ajustBitmap(bt); photo.setImageBitmap(disbitmapt); } Bitmap disbitmap = ajustBitmap(bitmap); photo.setImageBitmap(disbitmap); } } } /** * Корректировка направления изображения * @param bitmap * @return */ private Bitmap rotaingImageView(Bitmap bitmap) { // Действие поворота изображения Matrix matrix = new Matrix();; matrix.postRotate(270); // Создание нового изображения Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); return resizedBitmap; } /** * Получение пути к изображению * @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. The formation process of each small block of the puzzle.
Looking at each small block, using GridLayout to implement is the most convenient. So, use a GridLayout to display the small blocks cut from the large image, use an ImageView array to save the information of each small block, and by default, set the last small block as an empty block.
Firstly, there are various required variables. The comments are very clear.
/** *Create several game small boxes using a two-dimensional array */ private ImageView[][] iv_game_arr=new ImageView[3][5]; /** *Main game interface * */ private GridLayout gl_game_layout; //Rows and columns of small blocks private int i; private int j; /**Global variable for empty block*/ private ImageView iv_null_imagview;
Next, get the image from Imageview and cut it into certain rows and columns (here the puzzle is set to 3 rows and 5 columns). Store the information of each small block after cutting into an ImageView array. Set Tag and click listener for each small block.
private void setGameItem() { //Adjust the size of the image Bitmap abitmap=ajustBitmap(bt_tupan); int ivWidth=getWindowManager().getDefaultDisplay().getWidth()/5;//The width and height of each game small block. Cut into a square 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++){ //Cut the large image into small blocks 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); //Установить图案 каждого маленького блока iv_game_arr[i][j].setLayoutParams(new RelativeLayout.LayoutParams(ivWidth, ivWidth)); //Установить间距 между блоками iv_game_arr[i][j].setPadding(2, 2, 2, 2); iv_game_arr[i][j].setTag(new GameItemView(i, j, bm)); //Привязать пользовательские данные iv_game_arr[i][j].setOnClickListener(new View.OnClickListener() { ...... );
Конечно, выбранные нами изображения не могут быть всех стандартных размеров, поэтому перед нарезкой изображений их необходимо изменить. Установить изображение в пропорции 5:3 (только так можно нарезать 3 строки по 5 столбцов маленьких方块). Здесь для width я уже учел промежутки между маленькими方块ами.
//Установить размер изображения 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; }
Вставить каждый маленький方块 в GridLayout.
/** * Вставить маленькие方块 в GridLayout */ private void startGame() { tv_step.setText("Использованные шаги: 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]); } } //Установить последний блок в пустой блок. setNullImageView(iv_game_arr[i - 1][j - 1]);
5.小方块的点击事件和手势判断过程。
这里是拼图游戏的核心,弄懂了小方块的移动变化规律,也就弄懂了拼图游戏。
对于点击事件,首先拿到被点击的小方块的各种信息(位置、图案)和空白小方块的位置信息,判断被点击的小方块是否和空白小方块相邻,如果相邻,就移动交换数据(用TranslateAnimation来实现移动动画),如果不相邻则无操作。
a.判断点击方块和空白方块是否相邻的方法
/** * 判断当前点击的方块,是否和空方块相邻。 * @param imageView 当前点击的方块 * @return true:相邻。 false:不相邻。 */ public boolean isAdjacentNullImageView(ImageView imageView){ //获取当前空方块的位置与点击方块的位置 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()){//当前点击的方块在空方块的上方 return true; else if(null_gameItemView.getY() == now_gameItem_view.getY() && now_gameItem_view.getX() == null_gameItemView.getX() + 1){//当前点击的方块在空方块的下方 return true; else if(null_gameItemView.getY() == now_gameItem_view.getY() + 1 && now_gameItem_view.getX() == null_gameItemView.getX()){//当前点击的方块在空方块的左侧 return true; else if(null_gameItemView.getY() + 1 == now_gameItem_view.getY() && now_gameItem_view.getX() == null_gameItemView.getX()){ ////当前点击的方块在空方块的右侧 return true; } return false; }
b.如果相邻就进入方块数据交换的方法
这里有一个方法重载,是否需要动画效果,没有动画效果的数据交换是为初始化游戏时打乱拼图做准备。这里列出核心交换代码。每次交换后还要判断是否游戏胜利(即是否拼图完成~)。
//获取点击方块绑定的数据 GameItemView gameItemView = (GameItemView) itemimageView.getTag(); //将空方块的图案设置为点击方块 iv_null_imagview.setImageBitmap(gameItemView.getBm()); //获取空方块绑定的数据 GameItemView null_gameItemView = (GameItemView) iv_null_imagview.getTag(); //交换数据(将点击方块的数据传入空方块) null_gameItemView.setBm(gameItemView.getBm()); null_gameItemView.setP_x(gameItemView.getP_x()); null_gameItemView.setP_y(gameItemView.getP_y()); //设置当前点击的方块为空方块。 setNullImageView(itemimageView); if (isStart) { isGameWin(); //成功时,会弹出一个提示框。 }
c.交换时的动画设置
在设置动画时,首先判断移动的方向,根据方向设置不同的移动动画,然后监听动画完成后,进行数据交换操作。即上面b.如果相邻就进入方块数据交换的方法。最后执行动画。
//1.创建一个动画,设置方向,移动的距离 //判断方向,设置动画 if (itemimageView.getX() > iv_null_imagview.getX()) { //当前点击的方块在空方块的上方} //下滑 translateAnimation = new TranslateAnimation(0.1f, -itemimageView.getWidth(), 0.1f, 0.1f); } else if (itemimageView.getX() < iv_null_imagview.getX()) { // текущий нажатый блок находится снизу пустого блока //вверх boolean f=itemimageView.getX() < iv_null_imagview.getX(); //Log.i("нажатый блок","sssssssssssssssssssssssss"+f); translateAnimation = new TranslateAnimation(0.1f, itemimageView.getWidth(), 0.1f, 0.1f); } else if (itemimageView.getY() > iv_null_imagview.getY()) { // текущий нажатый блок находится слева от пустого блока //右移 translateAnimation=new TranslateAnimation(0.1f,0.1f,0.1f,-itemimageView.getWidth()); } else if (itemimageView.getY() < iv_null_imagview.getY()) { // текущий нажатый блок находится справа от пустого блока //左移 translateAnimation=new TranslateAnimation(0.1f,0.1f,0.1f,itemimageView.getWidth()); } //2.设置动画的各种参数 translateAnimation.setDuration(80); translateAnimation.setFillAfter(true); //3.设置动画的监听 translateAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { isAminMove=true; } @Override public void onAnimationEnd(Animation animation) { //动画结束,交换数据 ...... } //动画执行 itemimageView.startAnimation(translateAnimation);
Процесс обработки события нажатия завершен, дальше следуют события определения жеста. То есть не только можно передвигать квадратик нажатием, но и можно передвигать его жестом.
One. Создание объекта жеста
в методе onFling выполняются операции с жестом.
// создание объекта жеста 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 e2, float velocityX, float velocityY) { // операции с жестом ...... }
Далее мы выполняем конкретные действия в методе onFling
Two. Определение направления движения жеста
Разные значения возвращаемого значения определяют разные направления движения.
/** * добавление движения жеста, определение направления движения по жесту * @param start_x координата X начала жеста * @param start_y координата Y начала жеста * @param end_x координата X конца жеста * @param end_y координата Y конца жеста * @return 1: вверх 2: вниз 3: влево 4: вправо */ 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; // является ли это движение влево или вправо if(isLeftOrRight) { // Горизонтально boolean isLeft = (end_x - start_x) > 0 ? false : true; if(isLeft) { return 3; } else { return 4; } } else { // Вертикально boolean isUp = (end_y - start_y) > 0 ? false : true; if (isUp) { return 1; } else { return 2; } } }
Three. Определение возможности перемещения блока и выполнения перемещения на основе положения пустого блока и направления.
Поскольку это手势, перемещаемый肯定是 блоки вокруг пустого блока, поэтому的重点是要 определить, в каком направлении находится пустой блок, и затем, основываясь на этом направлении, определить, можно ли переместить блок, и выполнить перемещение. (Метод changeDateByImageView() содержит конкретную операцию по обмену данными и перемещению блоков. Это метод, связанный с событием щелчка.)
/** Перегрузка метода changeByDirGes(int type); * Перемещение блоков, смежных с пустым блоком, в зависимости от направления手势. * @param type Возврат значения направления 1: верх 2: вниз 3: лево 5: право * @param isAnim Имеется ли анимация true: есть анимация, false: нет анимации */ public void changeByDirGes(int type, boolean isAnim) { //1. Получение текущего положения пустого блока. GameItemView null_gameItemView = (GameItemView) iv_null_imagview.getTag(); int new_x = null_gameItemView.getX(); int new_y = null_gameItemView.getY(); //2. На основе направления устанавливается соответствующая相邻 координата. if (type==1) { // Указывает, что пустой блок над блоком, который нужно переместить. new_x++; } else if (type==2) { // Пустой блок снизу блока, который нужно переместить new_x--; } else if (type==3) { // Пустой блок слева от блока, который нужно переместить new_y++; } else if (type==4) { // Пустой блок справа от блока, который нужно переместить new_y--; } //3. Определение наличия новой координаты if(new_x>=0&&new_x<iv_game_arr.length&&new_y>=0&&new_y<iv_game_arr[0].length){ // существует, можно переместить и обменять данные if(isAnim){// есть анимация changeDateByImageView(iv_game_arr[new_x][new_y]); }else{ changeDateByImageView(iv_game_arr[new_x][new_y],isAnim); } }else{ // ничего не делать } }
Ну и手势овые события успешно завершены~
Конечно, здесь есть два момента, на которые стоит обратить внимание. 1. Сначала нужно установить метод onTouch() для текущей Activity, чтобы передать обработку Touch событий手势处理器, а также установить метод dispatchTouchEvent(), в котором также нужно направлять события手势向下. Если не направлять события手势向下, то в GridLayout можно будет только инициировать события клика, а手势овые события не будут работать. 2. Необходимо добавить флаг, указывающий на процесс перемещения, и если процесс перемещения正在进行, ничего не делать. В противном случае, каждый клик на маленький кубик даже в процессе перемещения будет инициировать событие клика, что приведет к повторному запуску анимации и создаст плохой пользовательский опыт.
@Override public boolean onTouchEvent(MotionEvent event) { return gestureDetector.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { gestureDetector.onTouchEvent(ev); return super.dispatchTouchEvent(ev); }
6. Методы для перемешивания кубиков при начале игры и для отображения Toast-подсказки в конце игры.
Код очень прост, сразу приведу код, среди которых, всплывающий Toast - это Toast с пользовательской анимацией View.
// случайное перемешивание порядка изображений public void randomOrder(){ // количество разменов, для удобства тестирования установлено очень маленькое. for (int i=0;i<5;i++){ // по手势у, обменивать данные, без анимации. int type = (int) (Math.random()*4)+1; // Log.i("sssssssssfdfdfd","交换次数"+i+"type的值"+type); changeByDirGes(type, false); } } /** *метод для определения завершения игры */ public void isGameWin(){ //знак победы в игре boolean isGameWin =true; //пройти через каждый маленький блок for (i = 0; i <iv_game_arr.length; i++){ for (j = 0; j <iv_game_arr[0].length; j++){ //пустые блоки не проверяются, пропускаются if (iv_game_arr[i][j]==iv_null_imagview){ continue; } GameItemView gameItemView= (GameItemView) iv_game_arr[i][j].getTag(); if (!gameItemView.isTrue()){ isGameWin=false; //выход из внутреннего цикла break; } } if (!isGameWin){ //выход из внешнего цикла break; } } //по переменной переключателя определяем, завершен ли игровой процесс, и показываем подсказку при завершении. if (isGameWin){ // Toast.makeText(this,"游戏胜利",Toast.LENGTH_SHORT).show(); ToastUtil.makeText(this,"恭喜你,游戏胜利,用了"+step+"步",ToastUtil.LENGTH_SHORT,ToastUtil.SUCCESS); step=0; } }
Теперь важная часть уже завершена, здесь также есть пользовательский View Toast. О подробностях Toast будет рассказано в следующей статье, здесь我们先简要说明自定义 Toast процесс.
Сначала, создадим класс SuccessToast (улыбка включает левый глаз, правый глаз, улыбка-дуга). Мы предоставим основные процессы. С помощью анимации реализуем динамический процесс рисования улыбки.
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setStyle(Paint.Style.STROKE); //улыбка笑脸弧 canvas.drawArc(rectF, 180, endAngle, false, mPaint); mPaint.setStyle(Paint.Style.FILL); if (isSmileLeft) { // если есть левый глаз, нарисовать левый глаз canvas.drawCircle(mPadding + mEyeWidth + mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint); } if (isSmileRight) { // если есть глаза, нарисовать правый глаз. canvas.drawCircle(mWidth - mPadding - mEyeWidth - mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint); } } /** * метод начала анимации * @param startF начальное значение * @param endF конечное значение * @param time время анимации * @return */ private ValueAnimator startViewAnim(float startF, final float endF, long time) { // установить начальное и конечное значения valueAnimator valueAnimator = ValueAnimator.ofFloat(startF, endF); // установить время анимации valueAnimator.setDuration(time); // установить интерполятор. Управлять скоростью изменения анимации valueAnimator.setInterpolator(new LinearInterpolator()); // установить слушатель. Наблюдать за изменением значения анимации и принимать соответствующие меры. valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { mAnimatedValue = (float) valueAnimator.getAnimatedValue(); // если значение value меньше 0.5 if (mAnimatedValue < 0.5) { isSmileLeft = false; isSmileRight = false; endAngle = -360 * (mAnimatedValue); //если значение value находится между 0.55 и 0.7 } endAngle = -180; isSmileLeft = true; isSmileRight = false; //другое } endAngle = -180; isSmileLeft = true; isSmileRight = true; } //перерисовка postInvalidate(); } }); if (!valueAnimator.isRunning()) { valueAnimator.start(); } return valueAnimator; }
Затем создайте новый файл success_toast_layout.xml, чтобы завершить布局 toast. Макет: слева smiling view, справа TextView с подсказкой.
<?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>
Создается новый класс ToastUtil для управления пользовательскими Toast.
/** * Создан yyh 25 октября 2016 года. */ 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(); } }
Таким образом, можно вызвать этот пользовательский Toast в ManiActivity.
Хорошо, все кончено.
игровой исходный код:Пазл играпроцесс реализации
gitHub:Пазл игра.
Вот и все, что есть в этой статье, надеюсь, это поможет вам в изучении. Также希望大家多多支持呐喊教程。
Заявление: содержимое статьи взято из Интернета, авторские права принадлежат правообладателям, материалы предоставлены пользователями Интернета, сайт не имеет права собственности, материалы не редактировались вручную, и не несет ответственности за связанные с этим юридические последствия. Если вы обнаружите подозрительное содержимое, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (при отправке письма замените # на @) для сообщения о нарушении авторских прав,并提供 соответствующие доказательства. При обнаружении нарушений мы немедленно удалим спорное содержимое.