Android app开发期末大作业“快乐七巧板”开发总结

写在前面

大二的暑假过去一半了,我终于想起来把期末大作业写出来了。本文的主要目的就是总结一学期安卓开发入门和最后期末大作业相关的一些收获和感受。

给自己一个总结和给以后再去学习的相关内容的小伙伴们一咪咪帮助吧。

这个app的技术核心是唐东明老师的移动终端设计(java)的一学期的跟进的儿童画板app设计作业,再加上调用了李晨玮前辈的贴纸的包。仔细看前辈的源码收获很多。

文章本身是期末设计报告的增强版,新增了更多的自己的想法和不想给老师看的一些内容

参考前辈的文章地址:https://www.jianshu.com/p/f1b0e4adae34 再次感谢?

  • 设计目的

因为平时做的是一个早教相关的作业,所以也想做相关的可以借用相关技术的app。
app本来想做成儿童可以设计少数民族服装的游戏,后来思考觉得需求太多难度太大我太菜就转变了想法,但是如果我变强了就随缘(你懂我意思吧)再去开发那个。

最后做出来的东西灵感来源于中国的传统拼图游戏七巧板,七块普通的积木块能拼出各种各样的图形。针对学龄前儿童,可以同通过次app提高自己的动手能力,发挥想象力用七巧板拼出不同的图形,认识不同的形状。达到寓教于乐的目的。不同于传统的积木,app中的积木可以随意变化,还可以添加特殊贴纸,让七巧板拼图更加具有趣味行和创造性。

  • 设计需求分析

  • 1. 界面之间的流畅跳转
  • 2. 示例图形的显示和滚动
  • 3. 七巧板拼图图形的显示及其放大,缩小,移动
  • 4. 附加装饰图形的显示及其放大,缩小,移动
  • 5. 画笔在已完成的拼图的涂鸦功能
  • 6 . 加入背景音乐
  • 7. 保存作品到本地相册
  • 8. 界面美观大方简洁,标示明确
  • 9. 将选择页面的实例图片传到拼图页面

因为我又懒又菜和时间紧迫的原因,部分需求并没有强制实现,暑假随缘(你懂我意思吧)完善。

  • 设计流程

因为本身界面就非常简单,这一部分也写的很简略。
只需要三个页面,欢迎页面,选择图形页面和拼图界面。点击第一个页面的开始按钮就跳转到第二个界面,选择好喜欢的图画以后可以跳转到第三个页面,然后就可以拼图。

Alt
第一个界面欢迎页面,童趣简洁。有app的名称和开始按钮。点击可以进入第二个页面,开始拼图。下图是第一个界面。
Alt

第二个页面上半部分是提示选择样例图形的文字,拖动一个textview放在了up-fragement里,下半部分是一个down-fragment,里面放置了recycleview,可以滚动查看图形。点击八个图其中的一个进行拼图,intent会把bitmap的imageId传给下一个activity,在下一个activity 的里的MySurfaceView示例再用intent接收图片,用bitmap绘制出来,即某一个示例图片会被传送到第三个界面,进行展示和比较。
点击图片后随即跳转到第三个页面,可以进行拼图活动。第二个界面如下。
在这里插入图片描述

第三个界面下半部分可以选择要拼图的图形,在整个屏幕都可以进行拼图操作。点击需要的图形就会在屏幕中间出现对应的图形,然后点击可以选中拖动,选定了某个拼图时候会出现选定的正方形框,左上角有可以点击删除的图标。当两点触摸的时候可以通过对应的变化,进行图片的按比例的放大、缩小和旋转。左侧还有额外的图形,可以进行的操作与下方拼图一致,让拼图更加有趣。

在stickerlibrary里面有一个接口iSupportOperation,里面放了对拼图的所以操作,包括平移、缩放、放大缩小、选定和删除功能。然后在BaseSticker这个抽象类里面对方法进行重写。
其中运用了很多矩阵类matrix的方法,比如旋转的时候实例化Matrix了以后调用了其中的postRotate方法,随后更新了拼图坐标点,实现了多次使用。

按比例放大用到的是postscale方法,也会及时调用updatePoints更新拼图坐标。
绘制拼图使用的onDraw,其中bitmap获取绘制内容,然后调用canvas绘制直线的drawline方法,以上边框为例,起点是图片左上角的横坐标减去预留边框padding的值,终点是右上角横坐标加上padding的值。

sticker类继承了basesticker,sticker里面计算了具体某一次变换的具体值,比如角度,移动距离,放大程度,然后调用父类的相关方法,完成一次变换。

在StickerManager里面实现对贴纸的选定和删除的管理。每一个贴纸选定添加了以后都会被放在一个ArrayList里面,在删除贴纸的时候,判断它被选定就可以在ArrayList被删除掉。
最后在StickerLayout 里面调用StickeManager里面的删除,添加等等功能。

在app的第三个页面的activity的xml文件里面放入自定义布局StickerLayout,每个拼图为一个imageview,为他们绑上监听器,每一个都是为一个实例化的sticker类。

Alt

  • 代码相关

  • 文件的基本结构
    欢迎界面welcome,第二个界面两个fragment分别为down和up,draw封装示例图片,first为第二个界面,里面调用俩fragment。
    因为最后分层的时候没有解决拼图和绘画两次的问题,虽然有绘画板那一套MysurfanceView但是并没有用(功能完善没舍得删)

在这里插入图片描述

  • welcome里面的内容

package com.lcw.view;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class welcome extends Activity {
    @Override
    protected  void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.wel);
        Button btn  =findViewById(R.id.go);
        //给开始按钮绑定跳转页面
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(welcome.this,First.class);
                //intent跳转到第一个界面
                startActivity(intent);
            }
        });
    }

}
  • 每个图片的draw
package com.lcw.view;

public class Draw {

    private  int imageId;
    //通过不同的id加载不同额图片
    public Draw(int imageId) {

        this.imageId=imageId;
    }

    public int getImageId() {

        return imageId;
    }
}

  • 第二个页面first
package com.lcw.view;

import android.content.Context;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

public class First extends AppCompatActivity  {
    private List<Draw> drawList = new ArrayList<>();
    private Context mContext = this;

    @Override
    public void onResume() {
        if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        }
        super.onResume();
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       setContentView(R.layout.first);

    initDraws();

    RecyclerView recyclerView = findViewById(R.id.recycle_view);
    GridLayoutManager layoutManager = new GridLayoutManager(this, 4);
    layoutManager.setOrientation(GridLayoutManager.VERTICAL);
    recyclerView.setLayoutManager(layoutManager);

        DrawAdapter adapter = new DrawAdapter(drawList);
        recyclerView.setAdapter(adapter);

}

    private void initDraws() {
    //加载示例图
        for (int i = 0; i < 1; i++) {
            Draw exp_1 = new Draw(R.drawable.exp_1);
            drawList.add(exp_1);
            Draw exp_2 = new Draw(R.drawable.exp_2);
            drawList.add(exp_2);
            Draw exp_3 = new Draw(R.drawable.exp_3);
            drawList.add(exp_3);
            Draw exp_4 = new Draw(R.drawable.exp_4);
            drawList.add(exp_4);
            Draw exp_5 = new Draw(R.drawable.exp_5);
            drawList.add(exp_5);
            Draw exp_6 = new Draw(R.drawable.exp_6);
            drawList.add(exp_6);
            Draw exp_7 = new Draw(R.drawable.exp_7);
            drawList.add(exp_7);
            Draw exp_8 = new Draw(R.drawable.exp_8);
            drawList.add(exp_8);



        }
    }


}

  • 简单粗暴调用贴纸lib!然后布局文件布局就是拼图的最后的界面

现在看来真是傻瓜式调用……具体的贴纸库去可以去前辈的文档仔细学习……

package com.lcw.view;

import android.Manifest;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.widget.ImageButton;

import com.lcw.library.stickerview.Sticker;
import com.lcw.library.stickerview.StickerLayout;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;


public class MainActivity extends AppCompatActivity {

    private StickerLayout mStickerLayout;
    MySurfaceView surfaceView;
    private View lastSelectPen = null;
    //private ImageView pucture;
    private boolean mPaintWidth = false;





    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        int imageId;
        final Intent intent = getIntent();
        /*使用getIntent()获取传过来的Intent对象,
         * Intent在激活一个Activity组建时会把Intent对象也传过来*/
        imageId = intent.getIntExtra("imageId", 0);
        /*该方法中的 defaultValue表示name对应的putExtra中没有传入有效的int类型值
         *就将defaultValue的值作为默认值传入*/
        surfaceView = (MySurfaceView)findViewById(R.id.surfaceview);
        surfaceView.setImageId(imageId);

        mStickerLayout = findViewById(R.id.sl_sticker_layout);
        surfaceView = (MySurfaceView)findViewById(R.id.surfaceview);
       

        findViewById(R.id.iv_sticker_01).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Sticker sticker = new Sticker(MainActivity.this, BitmapFactory.decodeResource(MainActivity.this.getResources(), R.mipmap.sticker_01));
                mStickerLayout.addSticker(sticker);
            }
        });

        findViewById(R.id.iv_sticker_02).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Sticker sticker = new Sticker(MainActivity.this, BitmapFactory.decodeResource(MainActivity.this.getResources(), R.mipmap.sticker_02));
                mStickerLayout.addSticker(sticker);
            }
        });

        findViewById(R.id.iv_sticker_03).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Sticker sticker = new Sticker(MainActivity.this, BitmapFactory.decodeResource(MainActivity.this.getResources(), R.mipmap.sticker_03));
                mStickerLayout.addSticker(sticker);
            }
        });

        findViewById(R.id.iv_sticker_04).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Sticker sticker = new Sticker(MainActivity.this, BitmapFactory.decodeResource(MainActivity.this.getResources(), R.mipmap.sticker_04));
                mStickerLayout.addSticker(sticker);
            }
        });
        findViewById(R.id.iv_sticker_05).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Sticker sticker = new Sticker(MainActivity.this, BitmapFactory.decodeResource(MainActivity.this.getResources(), R.mipmap.sticker_05));
                mStickerLayout.addSticker(sticker);
            }
        });

        findViewById(R.id.iv_sticker_06).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Sticker sticker = new Sticker(MainActivity.this, BitmapFactory.decodeResource(MainActivity.this.getResources(), R.mipmap.sticker_06));
                mStickerLayout.addSticker(sticker);
            }
        });

        findViewById(R.id.iv_sticker_07).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Sticker sticker = new Sticker(MainActivity.this, BitmapFactory.decodeResource(MainActivity.this.getResources(), R.mipmap.sticker_07));
                mStickerLayout.addSticker(sticker);
            }
        });

        findViewById(R.id.eye).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Sticker sticker = new Sticker(MainActivity.this, BitmapFactory.decodeResource(MainActivity.this.getResources(), R.mipmap.eye));
                mStickerLayout.addSticker(sticker);
            }
        });
        findViewById(R.id.pika).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Sticker sticker = new Sticker(MainActivity.this, BitmapFactory.decodeResource(MainActivity.this.getResources(), R.mipmap.pika));
                mStickerLayout.addSticker(sticker);
            }
        });

        findViewById(R.id.sun).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Sticker sticker = new Sticker(MainActivity.this, BitmapFactory.decodeResource(MainActivity.this.getResources(), R.mipmap.sun));
                mStickerLayout.addSticker(sticker);
            }
        });
        findViewById(R.id.star).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Sticker sticker = new Sticker(MainActivity.this, BitmapFactory.decodeResource(MainActivity.this.getResources(), R.mipmap.star));
                mStickerLayout.addSticker(sticker);
            }
        });
        findViewById(R.id.mouse).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Sticker sticker = new Sticker(MainActivity.this, BitmapFactory.decodeResource(MainActivity.this.getResources(), R.mipmap.mouse));
                mStickerLayout.addSticker(sticker);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mStickerLayout.removeAllSticker();
    }

    private void playAnim(View v){
        if(lastSelectPen!=null){
            lastSelectPen.startAnimation(AnimationUtils.loadAnimation(MainActivity.this,
                    R.anim.scale_zoom_in_anim));
            v.startAnimation(AnimationUtils.loadAnimation(MainActivity.this,
                    R.anim.scale_zoom_big_anim));
        }
        lastSelectPen=v;
    }
    
   //------------并没有实现的保存功能,有方法没有写button-------------------
   //-----------------------暂存-------------------

    //保存文件的方法:
    public void SaveBitmapFromView(View view) {
        int w = view.getWidth();
        int h = view.getHeight();
        Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmp);
        view.layout(0, 0, w, h);
        view.draw(c);
        // 缩小图片
        Matrix matrix = new Matrix();
        matrix.postScale(0.5f,0.5f); //长和宽放大缩小的比例
        bmp = Bitmap.createBitmap(bmp,0,0,        bmp.getWidth(),bmp.getHeight(),matrix,true);
        DateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
        saveBitmap(bmp,format.format(new Date())+".JPEG");
    }
    /*
     * 保存文件,文件名为当前日期
     */
    private void saveBitmap(Bitmap bitmap, String bitName){
        String fileName ;
        File file ;
        if(Build.BRAND .equals("Xiaomi") ){ // 小米手机
            fileName = Environment.getExternalStorageDirectory().getPath()+"/DCIM/Camera/"+bitName ;
        }else{ // Meizu 、Oppo
            fileName = Environment.getExternalStorageDirectory().getPath()+"/DCIM/"+bitName ;
        }
        file = new File(fileName);
        if(file.exists()){
            file.delete();
        }
        FileOutputStream out;
        try{
            out = new FileOutputStream(file);
            // 格式为 JPEG,照相机拍出的图片为JPEG格式的,PNG格式的不能显示在相册中
            if(bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out))
            {
                out.flush();
                out.close();
// 插入图库
                MediaStore.Images.Media.insertImage(this.getContentResolver(), file.getAbsolutePath(), bitName, null);
            }
        }
        catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        // 发送广播,通知刷新图库的显示
        this.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + fileName)));
    }
}

仍需改进

因为贴纸和画笔同时存在有点儿问题,以我目前的理解是因为贴纸和画笔并不在一层,两层操作不同,在同一块区域两层也不能同时有效。期末最后几天调试了好久也没有解决这个问题。
最后为了保全这个app的核心功能还是选择了拼图的一层。

闲言碎语

这学期同时学了web和安卓app开发,还有其他的一些选修,最后期末的时候熬了两周,觉得非常的充实好玩。成果还是比较满意的。
如果有学弟学妹偶然能看到我这个文章,我是要夸一波东明老师!请你们爱他!java是大一学了c以后更难更复杂的一个语言,对于我这样不竞赛不做项目的渣渣来说课堂的学习基本上占了大头。跟着东明学了两年下来觉得他是个务实又有趣的老师,跟着他认真完成作业会有很多收获,他是为数不多会对学生有所要求的老师。
平时凶了点但是还不是为了能多让学生学点java的东西,虽然我现在觉得python才是我的真爱

【以后随缘继续补充】

  • 13
    点赞
  • 116
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值