小妖拼图APP--项目总结

PS-本项目由我一人独立完成,尤其是图片分割规则的计算,本来想从网上找是否由相关的代码和思路,找了一圈发现没有,后来我自己摸索出来了一套计算的规则和方法,如果大家有什么想法我们可以互相讨论。
app下载地址:小妖拼图下载地址
      背景:
       最早接触android是从《第一行代码》开始的,看完后我做了一个课后联系天气预报APP。
    后来又慢慢啃完了《android群英传》,最后实战章节有个拼图游戏。但是书本上的联系太过简单,后来自己从网上看看别的拼图游戏,后来开发了这款小妖拼图的app。没有想到开发这个APP我自己就进入了一个大坑,拼图游戏的核心是对于图像的处理,这块的逻辑处理占用了项目的50%的时间。 期间又有一些新的想法和尝试,后来都无疾而终。

      不足:
       本来想做一个得分规则、排名和第三方登陆,这些需要用到后台Baas和第三方许可。而Baas服务需要花钱,被我PASS了;第三方登陆,现在申请创建移动应用都需要花钱搞软件著作权,也被我PASS。 顺便吐槽一句,现在个人开发者太难了。做什么都需要花钱,唉。

     话不多说,先上图看下程序的效果。

     代码梳理:
        1.MyApplication.

        初始化相册选择器,使用的是网上的开源的框架;初始化拼图对象池,否则在设置拼图块的时候,会不停的创建新的对象耗尽内存,导致程序很卡;初始化设置参数和相册。

public class MyApplication extends Application {
    private static Context mContext;
    private static int mScreenWidth;
    private static int mScreenHeight;

    private ImagePicker imagePicker;
    @Override
    public void onCreate() {
        super.onCreate();

        mContext = getApplicationContext();
        //初始化对象池
        PiecePool.getInstance();
        mScreenWidth = ScreenUtil.getScreenWidth(mContext);
        mScreenHeight = ScreenUtil.getScreenHeight(mContext);
        //

        //初始化相册选择参数
        imagePicker = ImagePicker.getInstance();
        imagePicker.setImageLoader(new PicassoImageLoader());
        imagePicker.setMultiMode(false);

        imagePicker.setStyle(CropImageView.Style.RECTANGLE);

        int width = 0;
        int height = 0;
        if (mScreenHeight > mScreenWidth) {
            width = mScreenWidth;
        } else {
            width = mScreenHeight;
        }
        height = width;
        imagePicker.setFocusWidth(width);
        imagePicker.setFocusHeight(height);

        imagePicker.setOutPutX(width);
        imagePicker.setOutPutY(height);
        imagePicker.setShowCamera(true);
        imagePicker.setCrop(true);
        imagePicker.setSaveRectangle(true);

        //只有当数据不存在的时候,再重新置初始值
        if(IntervalDatabase.getInstance(MyApplication.getContext()).getInterval() == 0){
            //置设置参数的初始值
            IntervalDatabase.getInstance(MyApplication.getContext()).add(7);//更新频率
            //初始相册
            List<String> list = new ArrayList<>();
            String[] albums = MyApplication.getContext().getResources().getStringArray(R.array.Albums);
            for(String name : albums) {
                list.add(name);
            }
            AlbumDatabase.getInstance(MyApplication.getContext()).add(list);
        }
    }

    public static Context getContext() {
        return mContext;
    }

    public static int getScreenWidth(){
        return mScreenWidth;
    }

    public static int getScreenHeight(){
        return mScreenHeight;
    }

}

    2.MainActivity
检查网络连接情况、相册是否从因特网爬取图片URL。若没有从因特网爬取图片URL,则进行爬取。检查数据库中的图片URL是否已经过期,若过期,则重新爬取图片URL。

public class MainActivity extends BaseActivity {
    public static Handler mHandler;

    private final static long UPDATE_UNIT = 24*60*60*1000; // 单位
    private static final String TAG = "MainActivity";
    //   private final static long UPDATE_UNIT = 60*1000; // 单位-测试用

    private int picture_num; //每个相册里面的照片数量

    private LinearLayout lv_loading;

    private LinearLayout lv_network;

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

        mHandler = new MyHandler();


        lv_network = (LinearLayout)findViewById(R.id.lv_network);
        lv_loading = (LinearLayout) findViewById(R.id.lv_loading);
        ImageView iv1 = (ImageView)findViewById(R.id.iv_guide_tip1);
        ImageView iv2 = (ImageView)findViewById(R.id.iv_guide_tip2);

        //缩放动画
        ScaleAnimation sa1 = new ScaleAnimation(0.5f,1,0.5f,1,
                Animation.RELATIVE_TO_SELF,0.5f,
                Animation.RELATIVE_TO_SELF,0.5f);
        sa1.setDuration(1000);
        sa1.setInterpolator(new BounceInterpolator());
        iv1.startAnimation(sa1);

        //缩放动画
        ScaleAnimation sa2 = new ScaleAnimation(0.5f,1,0.5f,1,
                Animation.RELATIVE_TO_SELF,0.5f,
                Animation.RELATIVE_TO_SELF,0.5f);
        sa2.setDuration(1000);
        sa2.setInterpolator(new BounceInterpolator());
        sa2.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                if(!NetworkUtil.isNetworkAvailable(MyApplication.getContext())){
                    Log.d(TAG, "isNetworkAvailable FAIL");
                    lv_network.setVisibility(View.VISIBLE);
                    //avi_process.hide();
                    lv_loading.setVisibility(View.GONE);
                } else {
                    Log.d(TAG, "isNetworkAvailable SUCCESS");
                    lv_network.setVisibility(View.GONE);
                    //avi_process.show();

                    lv_loading.setVisibility(View.VISIBLE);
                    if(checkIsNeedUpdate()){
                        GetPictureUrlService.startAction(mContext);
                    } else {
                        //不需要下载直接进入
                        startAction(HomeActivity.class);
                        finish();
                    }
                }
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
        iv2.startAnimation(sa2);


        picture_num = getResources().getInteger(R.integer.page_num);

    }

    //检查数量是否正确,规则是每个相册最起码包含一个子照片
    private boolean checkNum(){
        boolean isTrue = true;
        List<Album> list = UrlDatabase.getInstance(this).findAll(AlbumDatabase.getInstance(this).findAll());
        for (Album album : list) {
            if (album.getUrls().size() < 1) {
                isTrue = false;
                break;
            }
        }
        return isTrue;
    }

    private boolean checkIsNeedUpdate(){
        boolean flag=false;
        long currentTimeMillis =  UrlDatabase.getInstance(this).findCurrentTimeMillis();
        if(currentTimeMillis == 0) {
            flag = true;
        } else {
            //先判断下载的条数对不对,有可能后台返回的数目小于程序预置的数目
            if(!checkNum()) {
                flag = true;
            }else {
                //上次下载距离现在时间戳
                long now = System.currentTimeMillis();
                long times = now - UrlDatabase.getInstance(this).findCurrentTimeMillis();
                int update_date = IntervalDatabase.getInstance(this).getInterval();//更新频率
                if (times >= (update_date * UPDATE_UNIT)) {
                    flag = true;
                }
            }
        }
        return flag;
    }

    public void btnNetwork(View view) {
        if(!NetworkUtil.isNetworkAvailable(MyApplication.getContext())){
            Log.d(TAG, "isNetworkAvailable FAIL");
            lv_network.setVisibility(View.VISIBLE);
            //avi_process.hide();
            lv_loading.setVisibility(View.GONE);
        } else {
            Log.d(TAG, "isNetworkAvailable SUCCESS");
            lv_network.setVisibility(View.GONE);
            //avi_process.show();

            lv_loading.setVisibility(View.VISIBLE);
            if(checkIsNeedUpdate()){
                GetPictureUrlService.startAction(this);
            } else {
                //不需要下载直接进入
                startAction(HomeActivity.class);
                finish();
            }
        }
    }

    class MyHandler extends Handler{
        @Override
        public void handleMessage(final Message msg) {
            //下载已完成
            startAction(HomeActivity.class);
            finish();
        }
    }
}

3.HomeActivity
  分两部分,其一是固定部分(包括本地照片和设置);其二是从图片URL向网络获取bitmap,获取到的图片缓存到本地硬盘和缓存中(此处是借助郭大神的思想 Android照片墙完整版,完美结合LruCache和DiskLruCache ,大家可以去参考下)。

    HomeActivity的启动模式是singleTask模式,因为在设置Activity中会增加一个相册,增加完后会自动回退到HomeActivity,且需要将HomeActivity上面的Activity全部出栈。所以在onResume()方法中增加了对设置相册是否有变化的判断。

public class HomeActivity extends BaseActivity {
    private static final String YOUR_PICTURE = "您的照片";
    private static final String SETTING = "设置";

    ArrayList<ImageItem> images = null;
    private List<String> list;

    private List<HomeItem> mHomeItemList;
    private HomeAdapter mAadapter;

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == ImagePicker.RESULT_CODE_ITEMS) {
            if (data != null && requestCode == 100) {
                images = (ArrayList<ImageItem>) data.getSerializableExtra(ImagePicker.EXTRA_RESULT_ITEMS);

                Log.d("HomeActivity", "" + images.get(0).path);
                PictureBigActivity.startAction(this, true, images.get(0).path);
            } else {
                Toast.makeText(this, "没有数据", Toast.LENGTH_SHORT).show();
            }
        }
    }


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

        initData();

        final RecyclerView recycler_home = (RecyclerView)findViewById(R.id.recycler_home);
        recycler_home.setLayoutManager(new GridLayoutManager(this, 2));
        mAadapter = new HomeAdapter(HomeActivity.this, recycler_home, mHomeItemList, new HomeAdapter.SelectedItemListener() {
            @Override
            public void onSelected(String name) {
                if (name.equals(YOUR_PICTURE)){
                    Intent intent = new Intent(HomeActivity.this, ImageGridActivity.class);
                    intent.putExtra(ImageGridActivity.EXTRAS_IMAGES,images);
                    startActivityForResult(intent, 100);
                } else if (name.equals(SETTING)){
                    SettingActivity.startAction(mContext);
                } else {
                    AlbumActivity.startAction(HomeActivity.this, name);
                }
            }
        });


        recycler_home.setAdapter(mAadapter);
    }

    @Override
    protected void onResume() {
        super.onResume();
        boolean flag = false;
        //用于判断设置中是否增加了相册
        for (String name : AlbumDatabase.getInstance(mContext).findAll()) {
            Log.d("HomeActivity", name);
            if (!list.contains(name)) {
                //当set中不包含当前的数据的时候才会进行更新操作
                flag = true;
                list.add(name);
                String url = UrlDatabase.getInstance(this).findUrlsByName(name).get(0);
                HomeItem item = new HomeItem(name, url, HomeItem.TYPE_NORMAL, 0);
                mHomeItemList.add(item);
                Log.d("HomeActivity", "add name:" + name);
            }
        }
        if (flag) {
            Log.d("HomeActivity", "notifyDataSetChanged");
            mAadapter.notifyDataSetChanged();
        }
    }

    private void initData(){
        list = AlbumDatabase.getInstance(this).findAll();
        for (String name : list) {
            Log.d("HomeActivity", "initData:" + name);
        }
        mHomeItemList = new ArrayList<>();

        mHomeItemList.add(new HomeItem(YOUR_PICTURE, "", HomeItem.TYPE_HEAD, R.drawable.select_album));


        mHomeItemList.add(new HomeItem(SETTING, "", HomeItem.TYPE_HEAD, R.drawable.setting));

        //检查数据库是否存在数据
        List<Album> mAlbumList = UrlDatabase.getInstance(this).findAll(list);
        if (mAlbumList != null && mAlbumList.size() > 0) {
            for (Album album : mAlbumList) {
                HomeItem item = new HomeItem(album.getName(), album.getUrls().get(0), HomeItem.TYPE_NORMAL, 0);
                mHomeItemList.add(item);
            }
        }
    }

    public static void startAction(Context context) {
        Intent intent = new Intent(context, HomeActivity.class);
        context.startActivity(intent);
    }
}

4.AlbumActivity
   AlbumActivity使用从HomeActivity中传过来的图片名称,去本地数据库查找之前爬取的图片Url列表,然后传给RecyclerView进行加载图片的操作。

public class AlbumActivity extends BaseActivity {
    private int mPageNumbers;

    private static final String NAME = "album_name";//相册名称

    private AlbumAdapter mAadapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_album);
        initData();
        TextView tv_title = (TextView) findViewById(R.id.tv_title);

        Intent intent = getIntent();
        if(intent != null) {

            String name = intent.getStringExtra(NAME);

            Log.d("AlbumActivity", name+ ",num:" +mPageNumbers);

            tv_title.setText(name.split("-")[0]);

            List<String> list = UrlDatabase.getInstance(this).findUrlsByName(name);


            final RecyclerView recycler_album = (RecyclerView) findViewById(R.id.recycler_album);
            recycler_album.setLayoutManager(new GridLayoutManager(this, 2));
            mAadapter = new AlbumAdapter(AlbumActivity.this, recycler_album,list,  new AlbumAdapter.ClickListener() {
                @Override
                public void onClick(String url) {
                    PictureBigActivity.startAction(AlbumActivity.this, false, url);
                }
            });
            recycler_album.setAdapter(mAadapter);
        }
    }

    private void initData(){
        mPageNumbers  = getResources().getInteger(R.integer.page_num);
    }

    public static void startAction(Context context, String value) {
        Intent intent = new Intent(context, AlbumActivity.class);
        intent.putExtra("album_name",value);
        context.startActivity(intent);
    }
}

5.PictureBigActivity
   显示大图,并设置块数。首先判断图片来源是什么?是本地相册选择,还是网络爬取的图片。然后取得该图片的Bitmap对象,并传递给自定义图片控件(该控件根据穿入图片块数计算每个小块的分割线的路径规则,并取得路径,绘制出来)。

效果如下所示。
   

public class PictureBigActivity extends BaseActivity {
    private static final String TAG = "PictureBigActivity";
    private PieceImageView iv_big_album;
    private TextView tv_album_num;
    private DiscreteSeekBar sb_num;
    private ImageView iv_bg;

    private String mUrl;

    private boolean isFromAlbum;//是否来自于相册

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

        initViews();

        Intent intent = getIntent();
        if(intent != null) {

            mUrl = intent.getStringExtra("url");
            isFromAlbum = intent.getBooleanExtra("isFromAlbum", false);
            sb_num.setOnProgressChangeListener(new OnProgressChangeListener());

            tv_album_num.setText("块数:" + sb_num.getProgress()*sb_num.getProgress());

            iv_bg.post(new Runnable() {
                @Override
                public void run() {
                    setImageView();
                }
            });
        }
    }

    private void setImageView(){
        if(isFromAlbum){
            Bitmap bitmap = BitmapUtil.getBitmap(BitmapFactory.decodeFile(mUrl), iv_bg.getWidth(), iv_bg.getHeight());
            iv_big_album.setImageBitmap(bitmap);
        }else {
            ImageDownloader loader = new ImageDownloader(this);
            Bitmap bitmap = loader.getBitmapCache(mUrl);
            if (bitmap != null) {
                Log.d(TAG, "getBitmapCache OK");
                iv_big_album.setImageBitmap(bitmap);
            } else {
                loader.loadImage(mUrl, iv_bg.getWidth(), iv_bg.getHeight(),
                        new ImageDownloader.AsyncImageLoaderListener() {
                            @Override
                            public void onImageLoader(Bitmap bitmap) {
                                Log.d(TAG, "loadImage OK");
                                iv_big_album.setImageBitmap(bitmap);
                            }

                        });
            }
        }
    }

    private void initViews() {
        iv_big_album = (PieceImageView)findViewById(R.id.iv_big_album);
        tv_album_num = (TextView) findViewById(R.id.tv_album_num);
        sb_num = (DiscreteSeekBar)findViewById(R.id.sb_num);
        iv_bg = (ImageView)findViewById(R.id.iv_bg);
    }

    /**
     * 开始玩游戏
     * @param view
     */
    public void btnPlay(View view) {
        finish();
        GameActivity.startAction(this, isFromAlbum, mUrl, iv_big_album.getPieceSplit());
    }

    public static void startAction(Context context, boolean isFromAlbum, String value) {
        Intent intent = new Intent(context, PictureBigActivity.class);
        intent.putExtra("url",value);
        intent.putExtra("isFromAlbum",isFromAlbum);
        context.startActivity(intent);
    }

    class OnProgressChangeListener implements DiscreteSeekBar.OnProgressChangeListener {

        @Override
        public void onProgressChanged(DiscreteSeekBar seekBar, int value, boolean fromUser) {
            iv_big_album.setSpans(seekBar.getProgress()*seekBar.getProgress());
            tv_album_num.setText("块数:" + seekBar.getProgress()*seekBar.getProgress());
        }

        @Override
        public void onStartTrackingTouch(DiscreteSeekBar seekBar) {
        }

        @Override
        public void onStopTrackingTouch(DiscreteSeekBar seekBar) {
        }
    }
}
下面是自定义控件的代码。
public class PieceImageView extends android.support.v7.widget.AppCompatImageView {
    private static final String TAG = "PieceImageView";

    private int split_width;
    private Paint mBorderPaint;
    private Paint mBitmapPaint;
    private Paint mSplitPaint;

    private Context mContext;
    private int border_width;
    private int border_color;
    private int corner_radius;

    private int mSpans = 4;

    private List<Path> mPathList;
    private List<PieceSplit> mPieceSplitList;

    public PieceImageView(Context context) {
        this(context, null);
    }

    public PieceImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PieceImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;

        mPathList = new ArrayList<>();

        mBitmapPaint = new Paint();
        mBorderPaint = new Paint();
        mSplitPaint = new Paint();



        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PieceImageView);
        border_width = (int)ta.getDimension(R.styleable.PieceImageView_p_border_width, 1);
        border_color = ta.getColor(R.styleable.PieceImageView_p_border_color, mContext.getResources().getColor(R.color.gray_white));
        corner_radius = (int)ta.getDimension(R.styleable.PieceImageView_p_corner_radius, 4);
        ta.recycle();
        split_width = border_width;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        //防止同样块数的会重绘
        if(mPathList.size() == 0) {
            mPieceSplitList = PiecePathUtil.mathPath(getWidth(), getHeight(), mSpans);
            genPath(mPieceSplitList);
        } else {
            if(mPathList.size() != mSpans) {
                mPieceSplitList = PiecePathUtil.mathPath(getWidth(), getHeight(), mSpans);
                genPath(mPieceSplitList);
            }
        }

        Log.d(TAG, "onLayout");
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onDraw(Canvas canvas) {

        BitmapDrawable drawable = (BitmapDrawable)getDrawable();
        if(drawable == null) {
            Log.d("PieceImageView", "drawable == null");
            return;
        }

        Bitmap source = drawable.getBitmap();

        Bitmap squaredBitmap = BitmapUtil.getBitmap(source, getWidth(), getHeight());

        Rect rect = new Rect(0, 0, getWidth(), getHeight());
        RectF rectF = new RectF(rect);

        BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
        mBitmapPaint.setShader(shader);
        mBitmapPaint.setAntiAlias(true);

        //画图bitmap
        canvas.drawRoundRect(rectF,corner_radius,corner_radius,mBitmapPaint);

        //画外边框
        mBorderPaint.setStrokeWidth(border_width);
        mBorderPaint.setColor(border_color);
        mBorderPaint.setStyle(Paint.Style.STROKE);
        mBorderPaint.setAntiAlias(true);
        canvas.drawRoundRect(rectF,corner_radius,corner_radius,mBorderPaint);

        //画分割线
        mSplitPaint.setColor(border_color);
        mSplitPaint.setStyle(Paint.Style.STROKE);
        mSplitPaint.setAntiAlias(true);
        mSplitPaint.setStrokeWidth(split_width);


        for(Path path:mPathList) {
            canvas.drawPath(path, mSplitPaint);
        }
//        canvas.drawPath(mPathList.get(1),mSplitPaint);
        Log.d(TAG,"onDraw");
    }




    public void setSpans(int spans) {
        mSpans = spans;
        if(mSpans < 49) {
            split_width = border_width;
        } else if(mSpans < 64){
            split_width = border_width/2 == 0 ? 1 : border_width/2;
        } else {
            split_width = 1;
        }

        //重新计算路径
        mPieceSplitList = PiecePathUtil.mathPath(getWidth(), getHeight(), mSpans);
        genPath(mPieceSplitList);

        Log.d(TAG, "setSpans");
        invalidate();
    }

    public List<PieceSplit> getPieceSplit() {
        return mPieceSplitList;
    }

    private void genPath(List<PieceSplit> list){
        long begin = System.currentTimeMillis();
        PiecePool.getInstance().reset();
        mPathList.clear();

        for (PieceSplit pieceSplit : list) {
            mPathList.add(PiecePathUtil.getPath(pieceSplit));
        }
        Log.d("ms", "ms11:" + (System.currentTimeMillis() - begin));
    }
}

6.GameActivity
   首先还是取得图片的来源是哪并判断,然后取得Bitmap对象。其次,取得从PictureBigActivity中传过来的计算好的小图像块的路径规则列表。
    将路径规则和Bitmap对象进行计算,得到小图像块,最终位置,随机移动到的位置,并保存在对象列表中。

    在对象列表中分别取出对象,并创建自定义的移动自定义控件MoveImageView,同时为控件添加动画。动画完成后,则可以进行拼图游戏。
      拼图完成后会有动画提示拼图成功。
public class GameActivity extends BaseActivity {
    private static final String TAG = "GameActivity";
    private Context mContext;
    private String url;
    private PieceSplitList pieceSplitList;
    private ScaleFrameLayout sfl;
    //private FrameLayout fl;
    private PieceSplitImageView iv_game_main;
    private RelativeLayout rl_parent;
    private ImageView iv_end;
    private TextView tv_success_tip;

    private  boolean isFromAlbum;
    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_game);
        mContext = this;
        iv_game_main = (PieceSplitImageView)findViewById(R.id.iv_game_main);
        sfl = (ScaleFrameLayout)findViewById(R.id.sfl);
        rl_parent = (RelativeLayout)findViewById(R.id.rl_parent);
        iv_end = (ImageView) findViewById(R.id.iv_end);
        tv_success_tip = (TextView)findViewById(R.id.tv_success_tip);
        Intent intent = getIntent();
        if (intent != null) {
            url = intent.getStringExtra("url");
            isFromAlbum = intent.getBooleanExtra("isFromAlbum", false);
            pieceSplitList = (PieceSplitList)intent.getSerializableExtra("list");
            iv_game_main.setList(pieceSplitList.getList());
            sfl.post(new Runnable() {
                @Override
                public void run() {
                    setImageView();
                }
            });

            MoveImageSet.VIEW_LIST = new HashMap<>();
            MoveImageSet.VIEW_NUM = pieceSplitList.getList().size();
        }


    }

    private void setImageView(){
        if(isFromAlbum){
            Bitmap bitmap = BitmapUtil.getBitmap(BitmapFactory.decodeFile(url), sfl.getWidth(), sfl.getHeight());
            iv_game_main.setImageBitmap(bitmap);
            handleBitmap(bitmap);
        }else {
            ImageDownloader loader = new ImageDownloader(this);
            Bitmap bitmap = loader.getBitmapCache(url);
            if (bitmap != null) {
                Log.d(TAG, "getBitmapCache OK");
                iv_game_main.setImageBitmap(bitmap);
                handleBitmap(bitmap);
            } else {
                loader.loadImage(url, sfl.getWidth(), sfl.getHeight(),
                        new ImageDownloader.AsyncImageLoaderListener() {
                            @Override
                            public void onImageLoader(Bitmap bitmap) {
                                Log.d(TAG, "loadImage OK");
                                handleBitmap(bitmap);
                            }

                        });
            }
        }

    }

    private void handleBitmap(final Bitmap bitmap){
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "width:" + sfl.getWidth() + ",height:" + sfl.getHeight());
                final Bitmap source = BitmapUtil.getBitmap(bitmap, sfl.getWidth(), sfl.getHeight());
                float unit = (pieceSplitList.getList().get(0).getRight() - pieceSplitList.getList().get(0).getLeft()) / 3;
                float unit_element = unit / 5;
                float unit_width = unit * 2 / 3;

                final List<ClipPieceBitmap> pieceBitmaps = BitmapUtil.clipBitmap(mContext, source,
                        pieceSplitList.getList(),
                        sfl.getX(),
                        sfl.getY(),
                        unit_element + unit_width);

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        for (ClipPieceBitmap clipPieceBitmap : pieceBitmaps) {
                            addView(clipPieceBitmap);
                        }

                        iv_game_main.setVisibility(View.GONE);
                        iv_end.setImageBitmap(source);
                        iv_end.setVisibility(View.GONE);
                    }
                });
            }
        }).start();

    }

    private void addView(ClipPieceBitmap clipPieceBitmap){
        MoveImageView view = new MoveImageView(mContext);
        view.setImageBitmap(clipPieceBitmap.getBitmap());
        view.setX(clipPieceBitmap.getX());
        view.setY(clipPieceBitmap.getY());
        view.setEndX(clipPieceBitmap.getX());
        view.setEndY(clipPieceBitmap.getY());
        view.setIndex(clipPieceBitmap.getIndex());
        view.setListener(new MoveImageView.EndListener() {
            @Override
            public void onEnd() {

                if (MoveImageSet.isAllEnd()){
                    //拼图成功
                    MoveImageSet.allInvisible();
                    iv_end.setVisibility(View.VISIBLE);
                    tv_success_tip.setVisibility(View.VISIBLE);
                    ObjectAnimator alpha = ObjectAnimator.ofFloat(tv_success_tip, "alpha", 1f, 0.5f,0.9f);
                    alpha.setDuration(3000);
                    alpha.start();

                }
            }
        });
        //设置动画效果
        ObjectAnimator moveX = ObjectAnimator.ofFloat(view, "x", clipPieceBitmap.getX(),clipPieceBitmap.getAnimX());
        ObjectAnimator moveY = ObjectAnimator.ofFloat(view, "y", clipPieceBitmap.getY(),clipPieceBitmap.getAnimY());
        AnimatorSet set = new AnimatorSet();
        set.playTogether(moveX,moveY);
        set.setDuration(2000);
        set.start();
        rl_parent.addView(view);

        //根据索引存储到List表中
        MoveImageSet.VIEW_LIST.put(view.getIndex(), view);
    }

    public static void startAction(Context context, boolean isFromAlbum, String url, List<PieceSplit> list) {
        Intent intent = new Intent(context, GameActivity.class);
        intent.putExtra("url", url);
        intent.putExtra("isFromAlbum",isFromAlbum);
        intent.putExtra("list", new PieceSplitList(list));
        context.startActivity(intent);
    }
}
public class MoveImageView extends android.support.v7.widget.AppCompatImageView {
    private static final String TAG = "MoveImageView";
    private int index;
    private boolean isEnd;//是否已经移动到指定位置
    private float endX;
    private float endY;

    //当控件移动到指定位置附件时的误差
    private int distance;

    private int lastX = 0;
    private int lastY = 0;

    private EndListener listener;

    public MoveImageView(Context context) {
        this(context, null);
    }

    public MoveImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public MoveImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        distance = DisplayUtil.dip2px(context,20);
        isEnd = false;
    }

    public void setListener(EndListener listener) {
        this.listener = listener;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (isEnd){
            //如果已经移动到指定位置,则不允许再次移动了
            return true;
        }
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                this.bringToFront();//当按下图片的时候显示在最上层
                lastX = (int)event.getRawX();
                lastY = (int)event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                handleMove(event);
                break;
            case MotionEvent.ACTION_UP:
                handleUp();

                break;
        }
        return true;
    }

    private void handleUp(){


        //Log.d(TAG, "ACTION_UP");
        //Log.d(TAG,"x:"+getX()+",y:"+getY() + ",endX:" + getEndX() + ",endY:"+getEndY()+",distance:"+distance);
        if(Math.abs(getEndX()-getX()) <= distance && Math.abs(getEndY()-getY()) <= distance){
            if (PieceLocUtil.isLeftTop(index)
                    || PieceLocUtil.isRightTop(index, MoveImageSet.VIEW_NUM)
                    || PieceLocUtil.isLeftBottom(index, MoveImageSet.VIEW_NUM)
                    || PieceLocUtil.isRightBottom(index, MoveImageSet.VIEW_NUM)) {
                //如果是四个角,可已直接放置图片
                endMove();
            } else if (PieceLocUtil.isLeft(index, MoveImageSet.VIEW_NUM)){
                //如果是左边中间部分,则判断其上边或者下边或者右边是否已经放置好,放置好后从哪里进去
                if(MoveImageSet.VIEW_LIST.get(PieceLocUtil.getTopIndex(index, MoveImageSet.VIEW_NUM)).isEnd()
                        ||MoveImageSet.VIEW_LIST.get(PieceLocUtil.getBottomIndex(index, MoveImageSet.VIEW_NUM)).isEnd()
                        ||MoveImageSet.VIEW_LIST.get(PieceLocUtil.getRightIndex(index)).isEnd()){
                    endMove();
                }
            } else if(PieceLocUtil.isRight(index, MoveImageSet.VIEW_NUM)){
                //如果是右边中间部分,则判断其上边或者下边或者左边边是否已经放置好,放置好后从哪里进去
                if(MoveImageSet.VIEW_LIST.get(PieceLocUtil.getTopIndex(index, MoveImageSet.VIEW_NUM)).isEnd()
                        ||MoveImageSet.VIEW_LIST.get(PieceLocUtil.getBottomIndex(index, MoveImageSet.VIEW_NUM)).isEnd()
                        ||MoveImageSet.VIEW_LIST.get(PieceLocUtil.getLeftIndex(index)).isEnd()){
                    endMove();
                }

            } else if (PieceLocUtil.isTop(index, MoveImageSet.VIEW_NUM)){
                //上边中间部分,则判断其左边或者右边或者下边是否已经放置好
                if(MoveImageSet.VIEW_LIST.get(PieceLocUtil.getLeftIndex(index)).isEnd()
                        ||MoveImageSet.VIEW_LIST.get(PieceLocUtil.getRightIndex(index)).isEnd()
                        ||MoveImageSet.VIEW_LIST.get(PieceLocUtil.getBottomIndex(index, MoveImageSet.VIEW_NUM)).isEnd()){
                    endMove();
                }
            } else if (PieceLocUtil.isBottom(index, MoveImageSet.VIEW_NUM)){
                //下边中间部分,则判断其左边或者右边或者上边是否已经放置好
                if(MoveImageSet.VIEW_LIST.get(PieceLocUtil.getLeftIndex(index)).isEnd()
                        ||MoveImageSet.VIEW_LIST.get(PieceLocUtil.getRightIndex(index)).isEnd()
                        ||MoveImageSet.VIEW_LIST.get(PieceLocUtil.getTopIndex(index, MoveImageSet.VIEW_NUM)).isEnd()){
                    endMove();
                }
            } else {
                //中间部分,则根据上下左右是否有放置好的来判断是否可以放置
                if(MoveImageSet.VIEW_LIST.get(PieceLocUtil.getLeftIndex(index)).isEnd()
                        ||MoveImageSet.VIEW_LIST.get(PieceLocUtil.getRightIndex(index)).isEnd()
                        ||MoveImageSet.VIEW_LIST.get(PieceLocUtil.getTopIndex(index, MoveImageSet.VIEW_NUM)).isEnd()
                        ||MoveImageSet.VIEW_LIST.get(PieceLocUtil.getBottomIndex(index, MoveImageSet.VIEW_NUM)).isEnd()){
                    endMove();
                }
            }
        }
    }

    private void endMove() {
        setX(getEndX());
        setY(getEndY());
        SoundUtil.getInstance().playDing(R.raw.ding);
        isEnd = true;
        if(listener != null) {
            listener.onEnd();
        }
    }

    private void handleMove(MotionEvent event){
        int movieX = (int)event.getRawX() - lastX;
        int movieY = (int)event.getRawY() - lastY;
        if(Math.abs(movieX) > 1 || Math.abs(movieY) > 1) {
            int x = (int)getX() + movieX;
            int y = (int)getY() + movieY;

//                    Log.d(TAG, "view left:"+getLeft()+",right:"+getRight()+",top:"+getTop()+",bottom:"+getBottom()+",x:"+getX()+",y:"+getY()+
//                        ",width:" + getWidth() + ",height:"+getHeight() + ",movieX:" + movieX + ",movieY:" + movieY);

            if(x < 0) {
                x = 0;
            } else if (x > MyApplication.getScreenWidth() - getWidth()){
                x = MyApplication.getScreenWidth() - getWidth();
            }

            if(y < 0){
                y = 0;
            } else if(y > MyApplication.getScreenHeight() - getHeight()){
                y = MyApplication.getScreenHeight() - getHeight();
            }
            setX(x);
            setY(y);
        }

        lastX = (int)event.getRawX();
        lastY = (int)event.getRawY();
    }

    public float getEndX() {
        return endX;
    }

    public float getEndY() {
        return endY;
    }


    public int getIndex() {
        return index;
    }

    public boolean isEnd() {
        return isEnd;
    }

    public void setEndX(float endX) {
        this.endX = endX;
    }

    public void setEndY(float endY) {
        this.endY = endY;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public interface EndListener {
        public void onEnd();
    }
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值