仿今日头条和qq侧滑和智慧北京的小项目 3

仿今日头条和QQ侧滑和智慧北京的小项目3

本项目图片素材均来自今日头条,QQ侧滑没有使用Android原生的NavigationDrawer,而使用的是第三方SlidingMenu,原因是这个控件暂时没有仔细研究(后期会研究并写demo),项目整体可以说是使用了一个Activity加多个Fragment,全部采用沉寂式。

前文摘要:仿今日头条和QQ侧滑和智慧北京的小项目2

TabPager(NewsPager新闻页面对应的11个子页面)

此页面相对比较复杂,所以单独用一篇blog来说明都处理了哪些逻辑

从网络上获取json数据

private void getDataFromNet() {
        HttpUtils httpUtils = new HttpUtils();
        httpUtils.send(HttpRequest.HttpMethod.GET, url, new RequestCallBack<Object>() {
            @Override
            public void onSuccess(ResponseInfo<Object> responseInfo) {

                if (isRefresh) {
                    isRefresh = false;
                    listNews.onRefreshFinish(true);
                }

                Log.d(TAG, newsMenuTab.getTitle() + "数据请求成功" + responseInfo.result);
                //数据请求成功向本地保存一份
                CacheUtils.putString(mActivity, url, String.valueOf(responseInfo.result));
                //解析json
                resolutionJson((String) responseInfo.result);
            }

            @Override
            public void onFailure(HttpException e, String s) {
                if (isRefresh) {
                    listNews.onRefreshFinish(false);
                }
                Log.d(TAG, newsMenuTab.getTitle() + "数据请求失败");
            }
        });
    }

解析json数据

解析json数据分为若干部分,解析json、初始化数据、设置数据、处理和解析数据。

  • 解析json(javaBean在此不做赘述,获取json,直接生成即可)
private TopNewsBean topNewsJson(String result) {
        Gson gson = new Gson();
        TopNewsBean topNewsBean;
        topNewsBean = gson.fromJson(result, TopNewsBean.class);
        return topNewsBean;
    }
  • 初始化数据、设置数据
/**
     * 解析json数据
     *
     * @param result
     */
    private void resolutionJson(String result) {
        TopNewsBean topNewsBean = topNewsJson(result);
        if (!isLoadMore){
            //把解析json封装成一个方法这样看起来代码没那么乱
            topNewsList = topNewsBean.getData().getTopnews();

            moreUrl = topNewsBean.getData().getMore();
            if (TextUtils.isEmpty(moreUrl)) {
                moreUrl = null;
            }else {
                moreUrl = ConstantUtils.CONNECTURL+moreUrl;
            }
            //初始化顶部新闻的Viewpager数据

            //初始化Viewpager数据
            TopNewsTabAdapter topNewsTabAdapter = new TopNewsTabAdapter();
//        给ViewPager设置数据
            hvp.setAdapter(topNewsTabAdapter);
            hvp.setOnPageChangeListener(this);

            //初始化文字和点

            llPoint.removeAllViews();//因为访问网络读取缓存这个方法会被执行2此,所以需要要移除以前的view
            View view = null;
            for (int i = 0; i < topNewsList.size(); i++) {
                view = new View(mActivity);
                view.setBackgroundResource(R.drawable.point_seclect);
                LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(15, 15);
                if (i != 0) {
                    params.leftMargin = 15;
                }
                view.setEnabled(false);
                view.setLayoutParams(params);
                llPoint.addView(view);

            }
            //初始化第一个点和文字
            firstDescription = 0;
            tvTopNewsDes.setText(topNewsList.get(firstDescription).getTitle());
            llPoint.getChildAt(firstDescription).setEnabled(true);

            //初始化listview数据
            newsItem = topNewsBean.getData().getNews();
            listNewsAdapter = new ListNewsAdapter();
            listNews.setAdapter(listNewsAdapter);

            // TODO: 16/5/28    给Viewpager设置自动滑动
            //因为该方法会执行2次,所以需要清空一次
            if (myHandle==null){
                myHandle = new MyHandle();
            }else {
                myHandle.removeCallbacksAndMessages(null);
            }
            myHandle.postDelayed(new MyRunnable(),4000);
        }else {
            isLoadMore = false;
            List<TopNewsBean.DataBean.NewsBean> moreNewsItem = topNewsBean.getData().getNews();
            newsItem.addAll(moreNewsItem);
            listNewsAdapter.notifyDataSetChanged();
        }
    }
  • 处理和解析数据

1、轮播图数据

class TopNewsTabAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return topNewsList.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ImageView imageView = new ImageView(mActivity);

            //给imageview设置事件

            //设置默认图片和背景拉伸
            imageView.setScaleType(ImageView.ScaleType.FIT_XY);
            imageView.setBackgroundResource(R.drawable.default_bg);
            bitmapUtils = new BitmapUtils(mActivity);
            // 配置默认图片的像素单位
            bitmapUtils.configDefaultBitmapConfig(Bitmap.Config.ARGB_4444);
            //topimage的网络地址
            topNews = topNewsList.get(position);

            /**
             * container 下面的uri参数请求下来的图片, 设置给container来展示.
             * uri 图片的请求地址
             */
            bitmapUtils.display(imageView, topNews.getTopimage());
            container.addView(imageView);
            return imageView;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

    }

2、列表listview新闻数据

class ListNewsAdapter extends BaseAdapter {

        @Override
        public int getCount() {
            return newsItem.size();
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder = null;
            if (convertView == null) {
                convertView = View.inflate(mActivity, R.layout.listnews_item, null);
                viewHolder = new ViewHolder(convertView);
                convertView.setTag(viewHolder);
            }else {
                viewHolder = (ViewHolder) convertView.getTag();
            }
            TopNewsBean.DataBean.NewsBean newsBean = newsItem.get(position);
            viewHolder.tvListNews.setText(newsBean.getTitle());
            viewHolder.tvListDate.setText(newsBean.getPubdate());

            // 判断当前是否是已读的新闻
            String readableIDArray = CacheUtils.getString(mActivity, READABLE_NEWS_ID_ARRAY_KEY, null);
            // TODO: 16/5/27
            if(!TextUtils.isEmpty(readableIDArray)
                    && readableIDArray.contains(newsBean.getId()+"")) {
                viewHolder.tvListNews.setTextColor(Color.GRAY);
            } else {
                viewHolder.tvListNews.setTextColor(Color.BLACK);
            }

            //设置默认图片
            viewHolder.ivListNews.setBackgroundResource(R.drawable.listnews_default_bg);
            bitmapUtils.display(viewHolder.ivListNews,newsBean.getListimage());
            return convertView;
        }

         class ViewHolder {
            @Bind(R.id.iv_list_news)
            public ImageView ivListNews;
            @Bind(R.id.tv_list_news)
            public TextView tvListNews;
             @Bind(R.id.tv_list_date)
             public TextView tvListDate;

            ViewHolder(View view) {
                ButterKnife.bind(this, view);
            }
        }
    }

viewpager轮播新闻页面逻辑

设置点和文字的自动切换、自动轮播

设置点和文字的自动切换

1、给Viewpager设置页面滑动监听

2、初始化点和文字描述控件

 //初始化文字和点

 llPoint.removeAllViews();//因为访问网络读取缓存这个方法会被执行2此,所以需要要移除以前的view
            View view = null;
            for (int i = 0; i < topNewsList.size(); i++) {
                view = new View(mActivity);
                view.setBackgroundResource(R.drawable.point_seclect);
                LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(15, 15);
                if (i != 0) {
                    params.leftMargin = 15;
                }
                view.setEnabled(false);
                view.setLayoutParams(params);
                llPoint.addView(view);

            }
            //初始化第一个点和文字
            firstDescription = 0;
            tvTopNewsDes.setText(topNewsList.get(firstDescription).getTitle());
            llPoint.getChildAt(firstDescription).setEnabled(true);

3、主要实现onPageSelected方法

//Viewpager的滑动事件监听

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        //设置点的切换
        tvTopNewsDes.setText(topNewsList.get(position).getTitle());
        llPoint.getChildAt(firstDescription).setEnabled(false);
        llPoint.getChildAt(position).setEnabled(true);
        firstDescription = position;
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }

自动轮播(通过Handle实现,定时任务)

为什么执行2次,第一次从缓存中读取数据,并处理数据,第二次从网络上获取数据,并处理数据。

// TODO: 16/5/28    给Viewpager设置自动滑动
            //因为该方法会执行2次,所以需要清空一次
            if (myHandle==null){
                myHandle = new MyHandle();
            }else {
                myHandle.removeCallbacksAndMessages(null);
            }
            myHandle.postDelayed(new MyRunnable(),4000);
        }else {
            isLoadMore = false;
            List<TopNewsBean.DataBean.NewsBean> moreNewsItem = topNewsBean.getData().getNews();
            newsItem.addAll(moreNewsItem);
            listNewsAdapter.notifyDataSetChanged();
        }

//Handle
class MyHandle extends Handler{
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //用于处理消息
            int newCurrentItem = (hvp.getCurrentItem() + 1)%topNewsList.size();
            hvp.setCurrentItem(newCurrentItem);
            //消息处理完在重新发送,类似递归
            myHandle.postDelayed(new MyRunnable(),4000);
        }
    }

  //run方法
class MyRunnable implements Runnable{

        @Override
        public void run() {
            //发一条空的消息
            myHandle.sendEmptyMessage(0);
        }
    }

列表新闻listview页面逻辑处理(自定义Listview)

下拉刷新、上拉加载

写一个类继承自listview,添加自定义头布局和尾布局,监听滑动事件等。

以下贴出关键代码。

下拉刷新部分

添加头布局

/**
     * 添加头布局
     */
    private void initHeadView() {
        headView = View.inflate(getContext(), R.layout.refresh_headview, null);
        ButterKnife.bind(this, headView);
        this.addHeaderView(headView);
        //默认隐藏头布局
        headView.measure(0, 0);
        measuredHeight = headView.getMeasuredHeight();
        headView.setPadding(0, -measuredHeight, 0, 0);
        initAnimation();
    }

处理Listview的点击事件

/**
     * 重新onTouchEvent,处理点击事件
     * @param ev
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (downY == -1){
                    downY = (int) ev.getY();
                }
//                downY = (int) ev.getY();
            break;
            case MotionEvent.ACTION_MOVE:
                if (downY == -1){
                    downY = (int) ev.getY();
                }
                int moveY = (int) ev.getY();
                int diffY = moveY - downY;
                // 判断当前是否正在刷新中
                if(currentState == RELEASEREFRESH) {
                    // 当前正在刷新中, 不执行下拉, 直接跳出
                    break;
                }

                //如果是从上向下滑动,并且是第一个头布局,才进行下拉操作
                boolean isDisplay = isDisplaySecondHeaderView();
                if (diffY>0&&isDisplay) {
                    int piddingTop = -measuredHeight+diffY;
                    if (piddingTop >= 0 && currentState != RELEASEREFRESH){
                        Log.i(TAG, "进入释放刷新状态");
                        currentState = RELEASEREFRESH;
                        refreshState();
                    }else if (piddingTop<0  && currentState != DOWNREFRESH){
                        Log.i(TAG, "进入下拉刷新");
                        currentState = DOWNREFRESH;
                        refreshState();
                    }
                    headView.setPadding(0,piddingTop,0,0);
                    return true;
                }

                break;
            case MotionEvent.ACTION_UP:
                downY = -1;
                if(currentState == DOWNREFRESH) {
                    // 当前是下拉刷新, 把头布局的隐藏
                    headView.setPadding(0, -measuredHeight, 0, 0);
                } else if(currentState == RELEASEREFRESH) {
                    // 当前是释放刷新, 进入到正在刷新中的状态
                    currentState = INREFRESH;
                    refreshState();

                    headView.setPadding(0, 0, 0, 0);

//                     调用用户的回调事件, 刷新数据
                    if(mOnRefreshListener != null) {
                        mOnRefreshListener.onPullDownRefresh();
                    }
                }

            break;
        }
        return super.onTouchEvent(ev);

 //判断3种状态

 private void refreshState() {
        switch (currentState) {
            case DOWNREFRESH: //下拉刷新
                ivRefresh.startAnimation(downAnima);
                tvRefresh.setText("下拉刷新");
                break;
            case RELEASEREFRESH: //释放刷新
                ivRefresh.startAnimation(upAnima);
                tvRefresh.setText("松开刷新");
                break;
            case INREFRESH: //刷新中
                ivRefresh.clearAnimation();
                ivRefresh.setVisibility(View.INVISIBLE);
                pbBar.setVisibility(View.VISIBLE);
                tvRefresh.setText("正在刷新中..");
                break;
        }
    }

定义回调接口

public interface OnRefreshListener {

        /**
         * 当下拉刷新时触发此方法
         */
        public void onPullDownRefresh();

        public void onLoadingMore();
    }

上拉加载更多部分


/**
     * 添加角布局
     */
    private void initFooterView() {
        footView = View.inflate(getContext(), R.layout.listview_footerview, null);
        footView.measure(0,0);
        footViewHeight = footView.getMeasuredHeight();
        this.addFooterView(footView);
        footView.setPadding(0, -footViewHeight, 0, 0);
        this.setOnScrollListener(new OnScrollListener() {
            //当页面改变是调用
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                // 当滚动停止时, 或者惯性滑动时, ListView最后一个显示的条目索引为getCount -1;
                if(scrollState == SCROLL_STATE_IDLE ||
                        scrollState == SCROLL_STATE_FLING) {
                    if(getLastVisiblePosition() == getCount() -1 && !isLoadingMore) {
                        System.out.println("滚动到底部了");

                        isLoadingMore  = true;

                        footView.setPadding(0, 0, 0, 0);
                        // 让ListView滚动到底部
                        setSelection(getCount());

                        // 调用使用者的回调事件
                        if(mOnRefreshListener != null) {
                            mOnRefreshListener.onLoadingMore();
                        }
                    }
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            }
        });
    }

NewsDetailUI类(Listview页面item条目的事件处理)

点击listview条目进入一个新的页面,通过WebView展示一个web网页

listNews.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                int newsposition = position - 2;
                newsBeanItem = newsItem.get(newsposition);
                // 先把已读新闻的id取出来
                String readableIDArray = CacheUtils.getString(mActivity, READABLE_NEWS_ID_ARRAY_KEY, "");
                if(!readableIDArray.contains(newsBeanItem.getId()+"")) {
                    String currentID = null;
                    if(TextUtils.isEmpty(readableIDArray)) {
                        currentID = newsBeanItem.getId() + ", ";
                    } else {
                        currentID = readableIDArray + newsBeanItem.getId() + ", ";
                    }
                    // 把这条新闻的id存储起来
                    CacheUtils.putString(mActivity, READABLE_NEWS_ID_ARRAY_KEY, currentID);
                }

                listNewsAdapter.notifyDataSetChanged();

                Intent intent = new Intent(mActivity, NewsDetailUI.class);
                intent.putExtra("url", newsBeanItem.getUrl());
                intent.putExtra("title",newsBeanItem.getTitle());
                mActivity.startActivity(intent);
            }
        });

初始化数据

private void initData() {
        mIbFinish.setVisibility(View.VISIBLE);
        mIbTextSize.setVisibility(View.VISIBLE);
        mIbShare.setVisibility(View.VISIBLE);
        mTvTitle.setMaxWidth(600);
        mTvTitle.setMaxLines(1);
        mTvTitle.setEllipsize(TextUtils.TruncateAt.valueOf("END"));
        mTvTitle.setText(getIntent().getStringExtra("title"));
        String url = getIntent().getStringExtra("url");
        settings = mWebView.getSettings();
//        settings.setJavaScriptEnabled(true); // 启用javascript脚本
//        settings.setBuiltInZoomControls(true); // 启用界面上放大和缩小按钮
//        settings.setUseWideViewPort(true); // 启用双击放大, 双击缩小功能
        mWebView.setWebViewClient(new WebViewClient() {

            @Override
            public void onPageFinished(WebView view, String url) {
                mPbLoading.setVisibility(View.GONE);
            }
        });

        mWebView.loadUrl(url);
    }

设置字体大小

/**
     * 设置webView的字体大小
     */
    private void setTextSize() {
        AlertDialog.Builder ab = new AlertDialog.Builder(this);
        ab.setTitle("选择字体大小");
        String[] item = new String[]{"超大号字体","大号字体","正常字体","小号字体","超小号字体"};
        ab.setSingleChoiceItems(item, currentSelectTextSize, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                tempCurrent = which;
            }
        });
        ab.setNeutralButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                currentSelectTextSize = tempCurrent;
                changeTextSize();
            }
        });
        ab.setPositiveButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });
        ab.show();
    }

share分享(第三方sdk使用在这里暂时不做介绍,根据官方文档一步一步就行了)

/**
     * 一键社会化分享
     */
    private void mobShare() {
        ShareSDK.initSDK(this);
        OnekeyShare oks = new OnekeyShare();
        //关闭sso授权
        oks.disableSSOWhenAuthorize();

// 分享时Notification的图标和文字  2.5.9以后的版本不调用此方法
        //oks.setNotification(R.drawable.ic_launcher, getString(R.string.app_name));
        // title标题,印象笔记、邮箱、信息、微信、人人网和QQ空间使用
        oks.setTitle("智慧北京");
        // titleUrl是标题的网络链接,仅在人人网和QQ空间使用
        oks.setTitleUrl("http://sharesdk.cn");
        // text是分享文本,所有平台都需要这个字段
        oks.setText("我是分享文本");
        // imagePath是图片的本地路径,Linked-In以外的平台都支持此参数
        //oks.setImagePath("/sdcard/test.jpg");//确保SDcard下面存在此张图片
        // url仅在微信(包括好友和朋友圈)中使用
        oks.setUrl("http://sharesdk.cn");
        // comment是我对这条分享的评论,仅在人人网和QQ空间使用
        oks.setComment("我是测试评论文本");
        // site是分享此内容的网站名称,仅在QQ空间使用
        oks.setSite(getString(R.string.app_name));
        // siteUrl是分享此内容的网站地址,仅在QQ空间使用
        oks.setSiteUrl("http://sharesdk.cn");

// 启动分享GUI
        oks.show(this);
    }

PhotosPager(组图页面的实现)

简单几步,获取数据,解析数据

初始化数据

@Override
    public void initData() {
        bitmapUtils = new BitmapUtils(mActivity);
        bitmapUtils.configDefaultBitmapConfig(Bitmap.Config.ARGB_4444);

        url = ConstantUtils.PHOTOSURL;

        //先从缓存中读取数据
        String photosJsonData = CacheUtils.getString(mActivity, url, null);
        if (photosJsonData != null) {
            parserJsonData(photosJsonData);
        }
        //从网络上请求数据
        getDataFromNet();
    }

private void getDataFromNet() {
        HttpUtils httpUtils = new HttpUtils();
        httpUtils.send(HttpRequest.HttpMethod.GET, url, new RequestCallBack<Object>() {
            @Override
            public void onSuccess(ResponseInfo<Object> responseInfo) {
                //请求成功本地存一份json,解析json
//                Log.d(TAG, "onSuccess: "+responseInfo.result);
                String result = (String) responseInfo.result;
                parserJsonData(result);
                CacheUtils.putString(mActivity, url, result);
            }

            @Override
            public void onFailure(HttpException e, String s) {
                Log.e(TAG, "onFailure: 组图数据请求失败。" + e);
            }
        });
    }

解析数据

private void parserJsonData(String result) {
        PhotosBean photosBean = parserJson(result);
        //主要取得数据图片的url+title
        photoNews = photosBean.getData().getNews();

        //设置listview数据
        PhotoAdapter photoAdapter = new PhotoAdapter();
        llPhotos.setAdapter(photoAdapter);
    }

    private PhotosBean parserJson(String result) {
        Gson gson = new Gson();
        PhotosBean photosBean = gson.fromJson(result, PhotosBean.class);
        return photosBean;
    }

    class PhotoAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return photoNews.size();
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder = null;
            if (convertView == null) {
                viewHolder =  new ViewHolder();
                convertView = View.inflate(mActivity, R.layout.photos_list_item, null);
                viewHolder.ivPhotoPhotos = (ImageView) convertView.findViewById(R.id.iv_photo_photos);
                viewHolder.tvTitlePhotos = (TextView) convertView.findViewById(R.id.tv_title_photos);
                convertView.setTag(viewHolder);
            }else {
                viewHolder = (ViewHolder) convertView.getTag();
            }
            PhotosBean.DataBean.NewsBean newsBean = photoNews.get(position);
            viewHolder.tvTitlePhotos.setText(newsBean.getTitle());
            //设置默认图片
            viewHolder.ivPhotoPhotos.setImageResource(R.drawable.default_bg);
            bitmapUtils.display(viewHolder.ivPhotoPhotos,newsBean.getListimage());
            return convertView;
        }

        public class ViewHolder {
//            @Bind(R.id.iv_photo_photos)
            public ImageView ivPhotoPhotos;
//            @Bind(R.id.tv_title_photos)
            public TextView tvTitlePhotos;

        }
    }

切换视图

 /**
     * 用于切换视图的方法
     * @param ib
     */
    public void switchView(ImageButton ib) {
        //more没有切换是listv
        if (isSingleColumns) {
            llPhotos.setVisibility(View.VISIBLE);
            gvPhoto.setVisibility(View.GONE);
            llPhotos.setAdapter(new PhotoAdapter());
            isSingleColumns =false;
            ib.setImageResource(R.mipmap.icon_pic_list_type);
        }else {
            llPhotos.setVisibility(View.GONE);
            gvPhoto.setVisibility(View.VISIBLE);
            gvPhoto.setAdapter(new PhotoAdapter());
            isSingleColumns =true;
            ib.setImageResource(R.mipmap.icon_pic_grid_type);
        }
    }

至此,本小项目完成。其他页面暂不做实现,其处理逻辑参考“新闻中心”页面即可。

关于作者

- 个人主页:Hsia

- Email:[email protected]

- 项目地址:https://github.com/swordman20/Zhbj

时间: 2024-10-17 13:54:48

仿今日头条和qq侧滑和智慧北京的小项目 3的相关文章

vue2.0仿今日头条开源项目

vue-toutiao 这是用 vue.js 2.0 高仿 今日头条 的移动端项目,结合了原生app的部分功能以及网页版. 前言 本人是 今日头条 的重度用户,在学习vue.js过程中,在GitHub上看到了很多高仿webapp的好项目.由此在有了一定的技术积累后,开始构思使用Vue写今日头条,一是自己对于头条的喜爱,另外也是对于自己学习成果的检验. 技术栈 vue.js 2.0全家桶(vue.vuex.vue-router) axios.jsonp element-ui.iview vue-l

[转]灯灯小程序开发手记:仿今日头条(上)

本文转自:http://www.jianshu.com/p/a1e0b8abb12d 写在前面 新的一年,祝大家新年快乐!当然对于程序员来说,新的一年,也要有新的改变.因此灯灯决定凑热闹编写微信小程序啦! 上一篇文章<记一次小程序开发过程>中,灯灯大致写了下自己第一次开发小程序的感受和流程.这一次灯灯会详细记录下自己制作一个小程序的思路.遇到的问题.涉及到的代码等和大家分享.    视频教程地址:http://study.163.com/course/introduction.htm?cour

基于Vue 2.0高仿 &lt;今日头条&gt; 单页应用。

这是用 vue.js 2.0 高仿 今日头条 的移动端项目,结合了原生app的部分功能以及网页版. 技术栈 vue.js 2.0全家桶(vue.vuex.vue-router) axios.jsonp element-ui.iview vue-lazyload.animate.css.moment.flexible.js 在线地址 线上地址(预览地址) GitHub源码地址 说明 项目内定死 账号: admin, 密码: admin. 因为数据原因,首页请求的数据接口来自网页版今日头条,修改了一

仿今日头条的graidview拖动

下面先上这次实现功能的效果图:(注:这个效果图没有拖拽的时候移动动画,DEMO里面有,可以下载看看) 三.开发思路 1.  获取数据库中频道的列表,如果为空,赋予默认列表,并存入数据库,之后通过对应的适配器赋给对应的GridView 2.  2个GridView--(1.DragGrid   2. OtherGridView) DragGrid 用于显示我的频道,带有长按拖拽效果 OtherGridView用于显示更多频道,不带推拽效果 注:由于屏幕大小不一定,外层使用ScrollView,所以

iOS仿今日头条滑动导航

之前写了篇博客网易首页导航封装类.网易首页导航封装类优化,今天在前两个的基础上仿下今日头条. 1.网易首页导航封装类中主要解决了上面导航的ScrollView和下面的页面的ScrollView联动的问题,以及上面导航栏的便宜量. 2.网易首页导航封装类优化中主要解决iOS7以上滑动返回功能中UIScreenEdgePanGestureRecognizer与ScrollView的滑动的手势冲突问题. 今天仿今日头条滑动导航和网易首页导航封装类优化相似,这个也是解决手势冲突,UIPanGesture

iOS新闻应用源码,高仿今日头条源码等

iOS精选源码 城市列表选择 一款非常时尚的照片选择插件 优酷播放按钮动画 BRPickerView是iOS的选择器组件,主要包括:日期选择器.时... 选择位置坐下动画Demo BAButton 图片.文字.倒计时等 git 功能最全的 button 分类 企业级完整iOS项目-<新闻来了> 较为美观的多级展开列表 高仿今日头条6.2.6 Swift 简单画板的swift实现 iOS优质博客 创建一个私有的 Pods 详解 前言骚年,你听说过组件化吗?没有?但你一定玩过乐高玩具,乐高玩具本身

项目记录,仿今日头条app

项目记录,仿今日头条app,五六月份主要做的项目,第一版已经完成上架,二次开发正在进行中

Android 仿今日头条频道管理(下)(GridView之间Item的移动和拖拽)

前言 上篇博客我们说到了今日头条频道管理的操作交互体验,我也介绍了2个GridView之间Item的相互移动.详情请參考:Android 仿今日头条频道管理(上)(GridView之间Item的移动和拖拽) 今天把相对照较复杂的gridView的拖拽也记录下.在開始之前我们事先要了解下Android的事件分发机制.网上这方面的资料也比較多.由于自己定义控件大部分要用到事件分发机制的知识. 实现思路 要实现Item的拖拽.事实上并非真正要去拖拽GridView的Item.而是使用WindowMan

iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码

iOS精选源码 控制台开发调试神器GHConsole iOS 高度封装自适应表单(编辑及附件) 基于AVPlayer的视频播放器 仿alibaba分类 Swift 4 - 模仿今日头条 SGBrowserView一行代码弹出你定义的View 最适合练手的项目--壁纸宝贝 仿京东筛选 SGImageBrowser 一个调用方法快速实现任意一个或多个imageView..... ARKit射击小游戏 一句话实现iOS版本更新提示 iOS一个炫酷交互的第三方微博 CoreText 实现图文混排 登录页