├── .gitignore ├── 01github上的项目展示.gif ├── 02实际项目展示.gif ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── tandygong │ │ └── gzx │ │ └── nestscroll │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── tandygong │ │ │ └── gzx │ │ │ └── nestscroll │ │ │ ├── activitys │ │ │ ├── MainActivity.java │ │ │ └── NestScrollWithScrollViewActivity.java │ │ │ ├── adapters │ │ │ ├── SchoolDeatilAdapter.java │ │ │ └── SubTitleMenuListAdapter.java │ │ │ ├── enums │ │ │ ├── AnimStyle.java │ │ │ └── Page.java │ │ │ ├── fragments │ │ │ ├── ContainerSchoolInfoFragment.java │ │ │ ├── TabProjectInfoFragment.java │ │ │ └── TabSchoolInfoFragment.java │ │ │ ├── utils │ │ │ ├── ActivityUtils.java │ │ │ └── Constants.java │ │ │ └── views │ │ │ ├── AppWebView.java │ │ │ ├── AppWebViewClient.java │ │ │ ├── CustomDrawerLayout.java │ │ │ ├── CustomerNestedScrollView.java │ │ │ ├── ProgressBarWebView.java │ │ │ ├── PullToRefreshLayout.java │ │ │ └── Pullable.java │ └── res │ │ ├── anim │ │ ├── reverse_anim.xml │ │ ├── rotating.xml │ │ ├── slide_in_from_bottom.xml │ │ ├── slide_in_from_top.xml │ │ ├── slide_out_to_bottom.xml │ │ └── slide_out_to_top.xml │ │ ├── drawable │ │ ├── bg_black_radius_5.xml │ │ └── progressbar.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_nest_scroll_with_scroll_view.xml │ │ ├── fragment_project_info.xml │ │ ├── fragment_tab_school_info.xml │ │ ├── item_tips_titles.xml │ │ ├── layout_fragment_container_school_info.xml │ │ ├── load_more.xml │ │ └── refresh_head.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ ├── guanliqi_back.png │ │ ├── guanliqi_xuanxiao.png │ │ ├── ic_launcher.png │ │ ├── load_failed.png │ │ ├── load_succeed.png │ │ ├── loading.png │ │ ├── pull_icon_big.png │ │ ├── pullup_icon_big.png │ │ ├── refresh_failed.png │ │ ├── refresh_succeed.png │ │ ├── refreshing.png │ │ ├── school_location.png │ │ ├── school_ranking.png │ │ ├── school_statistics.png │ │ ├── school_weather.png │ │ └── strategy_list.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── refs.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── tandygong │ └── gzx │ └── nestscroll │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | #idea 2 | .idea/ 3 | *.iml 4 | 5 | # Built application files 6 | *.apk 7 | *.ap_ 8 | 9 | # Files for the Dalvik VM 10 | *.dex 11 | 12 | # Java class files 13 | *.class 14 | 15 | # Generated files 16 | bin/ 17 | gen/ 18 | 19 | # Gradle files 20 | .gradle/ 21 | build/ 22 | */build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | /captures 34 | -------------------------------------------------------------------------------- /01github上的项目展示.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tandygong/NestScroll/c28e8fd7a5700a896f7c8c7a9ff746ee418259d4/01github上的项目展示.gif -------------------------------------------------------------------------------- /02实际项目展示.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tandygong/NestScroll/c28e8fd7a5700a896f7c8c7a9ff746ee418259d4/02实际项目展示.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #NestScroll 2 | ##这是从我做过的一个项目里面的一个页面 3 | 4 | ###使用了NestScrollView,PullToRefreshLayout,WebView多层嵌套 5 | #### 如有建议和问题请发送邮件至531493235@qq.com 6 | 7 | 8 | ####原项目效果 9 | ![1](https://raw.githubusercontent.com/tandygong/NestScroll/master/02%E5%AE%9E%E9%99%85%E9%A1%B9%E7%9B%AE%E5%B1%95%E7%A4%BA.gif) 10 | 11 | ####gitHub效果展示 12 | ![2](https://raw.githubusercontent.com/tandygong/NestScroll/master/01github%E4%B8%8A%E7%9A%84%E9%A1%B9%E7%9B%AE%E5%B1%95%E7%A4%BA.gif) 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.tandygong.gzx.nestscroll" 9 | minSdkVersion 16 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.1.1' 26 | compile 'com.android.support:design:23.1.1' 27 | } 28 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\java\SDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/tandygong/gzx/nestscroll/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/activitys/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.activitys; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.widget.Button; 7 | import android.widget.Toast; 8 | 9 | import com.tandygong.gzx.nestscroll.R; 10 | import com.tandygong.gzx.nestscroll.utils.ActivityUtils; 11 | 12 | /** 13 | * Created by usb on 2016/4/13. 14 | */ 15 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 16 | 17 | private Button btnScrollView; 18 | private Button btnRecycleView; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_main); 24 | findIds(); 25 | initData(); 26 | setListeners(); 27 | } 28 | 29 | 30 | private void findIds() { 31 | btnScrollView = (Button) findViewById(R.id.btn_use_scrollview); 32 | btnRecycleView = (Button) findViewById(R.id.btn_use_recycle_view); 33 | } 34 | 35 | private void initData() { 36 | 37 | } 38 | 39 | private void setListeners() { 40 | btnScrollView.setOnClickListener(this); 41 | btnRecycleView.setOnClickListener(this); 42 | } 43 | 44 | @Override 45 | public void onClick(View v) { 46 | switch (v.getId()) { 47 | case R.id.btn_use_scrollview: 48 | String url = "http://p3.qhimg.com/t01d0b5e6e40b60df20.png"; 49 | ActivityUtils.toSchoolDetailActivityForResult(this, true, "北京大学", "BeijingUniversity", url, 100, 1, true); 50 | break; 51 | case R.id.btn_use_recycle_view: 52 | Toast.makeText(this, "正在开发", Toast.LENGTH_SHORT).show(); 53 | break; 54 | } 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/activitys/NestScrollWithScrollViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.activitys; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.TabLayout; 5 | import android.support.v4.view.ViewPager; 6 | import android.support.v4.widget.DrawerLayout; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.View; 9 | import android.widget.ImageView; 10 | import android.widget.LinearLayout; 11 | import android.widget.ListView; 12 | import android.widget.RelativeLayout; 13 | import android.widget.TextView; 14 | 15 | import com.tandygong.gzx.nestscroll.R; 16 | import com.tandygong.gzx.nestscroll.adapters.SchoolDeatilAdapter; 17 | 18 | 19 | public class NestScrollWithScrollViewActivity extends AppCompatActivity implements View.OnClickListener, ViewPager.OnPageChangeListener { 20 | private TabLayout mTabLayout; 21 | private ViewPager mViewPager; 22 | private boolean isApply; 23 | private String englishName; 24 | private String chineseName; 25 | private String imgUrl; 26 | private int schoolId; 27 | private RelativeLayout rlShowSlideMenu; 28 | private ListView lvMenu; 29 | private DrawerLayout drawerLayout; 30 | private TextView tvContentNum; 31 | private LinearLayout llSearchSchool; 32 | private View applyBtn; 33 | private TextView tSchool; 34 | private View backBtn; 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | getExtraData(); 40 | setContentView(R.layout.activity_nest_scroll_with_scroll_view); 41 | findViews(); 42 | setData(); 43 | setListeners(); 44 | } 45 | 46 | public void getExtraData() { 47 | Bundle extras = getIntent().getExtras(); 48 | if (extras == null) { 49 | return; 50 | } 51 | isApply = extras.getBoolean("isApply", false); 52 | englishName = extras.getString("englishName"); 53 | chineseName = extras.getString("chineseName"); 54 | imgUrl = extras.getString("imgUrl"); 55 | schoolId = extras.getInt("schoolId"); 56 | } 57 | 58 | private void findViews() { 59 | mTabLayout = (TabLayout) findViewById(R.id.tabs); 60 | mViewPager = (ViewPager) findViewById(R.id.vp_main); 61 | ImageView tvToolBarBg = (ImageView) findViewById(R.id.toobar_bg); 62 | TextView tvNameEn = (TextView) findViewById(R.id.tv_name_english); 63 | TextView tvNameCn = (TextView) findViewById(R.id.tv_name_chinese); 64 | backBtn = findViewById(R.id.toobar_back); 65 | applyBtn = findViewById(R.id.toobar_apply); 66 | 67 | rlShowSlideMenu = (RelativeLayout) findViewById(R.id.rl_show_slide_menu); 68 | lvMenu = (ListView) findViewById(R.id.lv_menuList); 69 | tvContentNum = (TextView) findViewById(R.id.tv_contentNum); 70 | drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); 71 | 72 | 73 | //ImageLoader.getInstance().displayImage(imgUrl, tvToolBarBg); 74 | tvNameCn.setText(chineseName); 75 | tvNameEn.setText(englishName); 76 | 77 | 78 | } 79 | 80 | private void setData() { 81 | mViewPager.setAdapter(new SchoolDeatilAdapter(getSupportFragmentManager(), schoolId)); 82 | drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); 83 | rlShowSlideMenu.setVisibility(View.INVISIBLE); 84 | mTabLayout.setupWithViewPager(mViewPager); 85 | } 86 | 87 | private void setListeners() { 88 | mViewPager.addOnPageChangeListener(this); 89 | backBtn.setOnClickListener(this); 90 | applyBtn.setOnClickListener(this); 91 | } 92 | 93 | public ListView getLvMenu() { 94 | return lvMenu; 95 | } 96 | 97 | public DrawerLayout getDrawerLayout() { 98 | return drawerLayout; 99 | } 100 | 101 | public TextView getTvContentNum() { 102 | return tvContentNum; 103 | } 104 | 105 | public RelativeLayout getRlShowSlideMenu() { 106 | return rlShowSlideMenu; 107 | } 108 | 109 | 110 | @Override 111 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 112 | 113 | } 114 | 115 | @Override 116 | public void onPageSelected(int position) {//当切换tab时设置悬浮按钮显示/隐藏,DrawLayout锁定/解锁 117 | if (position == 0) { 118 | rlShowSlideMenu.setVisibility(View.INVISIBLE); 119 | drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); 120 | } else { 121 | rlShowSlideMenu.setVisibility(View.VISIBLE); 122 | drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); 123 | } 124 | } 125 | 126 | @Override 127 | public void onPageScrollStateChanged(int state) { 128 | 129 | } 130 | 131 | @Override 132 | public void onClick(View v) { 133 | switch (v.getId()) { 134 | case R.id.toobar_back: 135 | finish(); 136 | break; 137 | case R.id.toobar_apply: 138 | break; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/adapters/SchoolDeatilAdapter.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.adapters; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.app.FragmentManager; 6 | import android.support.v4.app.FragmentPagerAdapter; 7 | 8 | import com.tandygong.gzx.nestscroll.fragments.TabProjectInfoFragment; 9 | import com.tandygong.gzx.nestscroll.fragments.TabSchoolInfoFragment; 10 | 11 | /** 12 | * viewPager的adapter,含有两个fragment 13 | */ 14 | public class SchoolDeatilAdapter extends FragmentPagerAdapter { 15 | private int schoolId; 16 | 17 | public SchoolDeatilAdapter(FragmentManager fm, int schoolId) { 18 | super(fm); 19 | this.schoolId = schoolId; 20 | } 21 | 22 | 23 | @Override 24 | public Fragment getItem(int position) { 25 | if (position == 1) { 26 | TabSchoolInfoFragment tabSchoolInfoFragment = new TabSchoolInfoFragment();//右侧的学校信息fragment 27 | Bundle bundle = new Bundle(); 28 | bundle.putInt("schoolId", schoolId); 29 | tabSchoolInfoFragment.setArguments(bundle); 30 | return tabSchoolInfoFragment; 31 | } else { 32 | TabProjectInfoFragment tabProjectInfoFragment = new TabProjectInfoFragment();//左侧的项目信息fragment 33 | return tabProjectInfoFragment; 34 | } 35 | } 36 | 37 | @Override 38 | public int getCount() { 39 | return 2; 40 | } 41 | 42 | @Override 43 | public CharSequence getPageTitle(int position) { 44 | return position == 0 ? "项目信息" : "学校信息"; 45 | } 46 | 47 | /* @Override 48 | public long getItemId(int position) { 49 | int hashCode = dataList.get(position).hashCode(); 50 | return hashCode; 51 | }*/ 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/adapters/SubTitleMenuListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.adapters; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.TextView; 9 | 10 | 11 | import com.tandygong.gzx.nestscroll.R; 12 | 13 | import java.util.ArrayList; 14 | 15 | /** 16 | * Created by gzx on 2016/3/11 at 20:42. 17 | */ 18 | public class SubTitleMenuListAdapter extends BaseAdapter { 19 | private int selectPos; 20 | private ArrayList dataList; 21 | private Context context; 22 | 23 | public SubTitleMenuListAdapter(Context context, ArrayList dataList) { 24 | this.context = context; 25 | this.dataList = dataList; 26 | } 27 | 28 | public int getSelectPos() { 29 | return selectPos; 30 | } 31 | 32 | public void setSelectPos(int selectPos) { 33 | this.selectPos = selectPos; 34 | notifyDataSetChanged(); 35 | } 36 | 37 | @Override 38 | public int getCount() { 39 | return dataList == null ? 0 : dataList.size(); 40 | } 41 | 42 | @Override 43 | public Object getItem(int position) { 44 | return dataList.get(position); 45 | } 46 | 47 | @Override 48 | public long getItemId(int position) { 49 | return position; 50 | } 51 | 52 | @Override 53 | public View getView(int position, View convertView, ViewGroup parent) { 54 | ViewHolder holder; 55 | if (convertView == null) { 56 | holder = new ViewHolder(); 57 | convertView = View.inflate(context, R.layout.item_tips_titles, null); 58 | holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title); 59 | convertView.setTag(holder); 60 | } else { 61 | holder = (ViewHolder) convertView.getTag(); 62 | } 63 | holder.tvTitle.setText(dataList.get(position)); 64 | if (position == selectPos) { 65 | holder.tvTitle.setTextColor(Color.parseColor("#ff8570")); 66 | holder.tvTitle.setBackgroundColor(Color.parseColor("#f6f6f6")); 67 | } else { 68 | holder.tvTitle.setTextColor(Color.parseColor("#222222")); 69 | holder.tvTitle.setBackgroundColor(Color.parseColor("#ffffff")); 70 | } 71 | 72 | return convertView; 73 | } 74 | 75 | class ViewHolder { 76 | TextView tvTitle; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/enums/AnimStyle.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.enums; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 切换内容时的动画 7 | * Created by gzx on 2016/3/17 at 15:01. 8 | */ 9 | public enum AnimStyle implements Serializable { 10 | /** 11 | * 从上往下移动 12 | */ 13 | TOP_TO_BOTTOM, 14 | /** 15 | * 从底部往上滑动 16 | */ 17 | BOTTOM_TO_TOP, 18 | /** 19 | * 默认加载,无动画 20 | */ 21 | DEFAULT_NO_ANIM 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/enums/Page.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.enums; 2 | 3 | /** 4 | * Created by gzx on 2016/3/17 at 14:56. 5 | */ 6 | public enum Page { 7 | /** 8 | * 第一页 9 | */FirstPage, 10 | /** 11 | * 最后一页 12 | */LastPage, 13 | /** 14 | * 中间页 15 | */CenterPage 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/fragments/ContainerSchoolInfoFragment.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.fragments; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v4.app.Fragment; 8 | import android.support.v4.widget.NestedScrollView; 9 | import android.util.Log; 10 | import android.view.LayoutInflater; 11 | import android.view.MotionEvent; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.LinearLayout; 15 | import android.widget.TextView; 16 | 17 | import com.tandygong.gzx.nestscroll.R; 18 | import com.tandygong.gzx.nestscroll.enums.AnimStyle; 19 | import com.tandygong.gzx.nestscroll.enums.Page; 20 | import com.tandygong.gzx.nestscroll.utils.Constants; 21 | import com.tandygong.gzx.nestscroll.views.CustomerNestedScrollView; 22 | import com.tandygong.gzx.nestscroll.views.ProgressBarWebView; 23 | import com.tandygong.gzx.nestscroll.views.PullToRefreshLayout; 24 | 25 | 26 | /** 27 | * 学校信息 28 | * Created by gzx on 2016/3/25 at 11:05. 29 | */ 30 | public class ContainerSchoolInfoFragment extends Fragment implements View.OnTouchListener, NestedScrollView.OnScrollChangeListener, PullToRefreshLayout.OnRefreshListener, View.OnLayoutChangeListener { 31 | private CustomerNestedScrollView mScrollView; 32 | private ProgressBarWebView webView; 33 | private String pageUrl; 34 | private int pageId; 35 | private Page currPage;//当前页的标识 第一页/最后一页/中间页 36 | private PullToRefreshLayout pulltoRefresh; 37 | private Intent receiverIntent; 38 | private TextView tvRealTitle; 39 | private TextView tvFakeTitle; 40 | private int[] locationReal = new int[2];//记录真标题栏在窗口位置的数组 41 | private int[] locationFake = new int[2];//记录假标题栏在窗口位置的数组 42 | private String subTitle; 43 | private LinearLayout llItemWhole; 44 | 45 | @Nullable 46 | @Override 47 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 48 | receiverIntent = new Intent(); 49 | receiverIntent.setAction(Constants.ACTION_REPLACE_FRAGMENT_SCHOOL_INFO); 50 | 51 | View view = inflater.inflate(R.layout.layout_fragment_container_school_info, null); 52 | getBundleData(); 53 | findViewIds(view); 54 | setListeners(); 55 | initData(); 56 | return view; 57 | } 58 | 59 | private void getBundleData() { 60 | Bundle extras = getArguments(); 61 | if (extras != null) {//从TabSchoolInfoFragment获取数据 62 | pageUrl = extras.getString("PageUrl"); 63 | subTitle = extras.getString("subTitle"); 64 | int totalCount = extras.getInt("TotalCount"); 65 | pageId = extras.getInt("PageId"); 66 | 67 | 68 | //如果页码(pageid 从0开始)等于0,当前页标识=FirstPage 69 | //如果页码和(总页数-1)相等,当前页标识=LastPage 70 | //其它值 ,当前页标识=CenterPage 71 | currPage = pageId == 0 ? Page.FirstPage : pageId == totalCount - 1 ? Page.LastPage : Page.CenterPage; 72 | 73 | Log.e("pageUrl", pageUrl); 74 | } 75 | 76 | 77 | } 78 | 79 | private void findViewIds(View view) { 80 | pulltoRefresh = (PullToRefreshLayout) view.findViewById(R.id.ptf_detail); 81 | mScrollView = (CustomerNestedScrollView) view.findViewById(R.id.nestScrollView);//实现PullAble接扣得NestScrollView 82 | webView = (ProgressBarWebView) view.findViewById(R.id.webView);//包含在PUllToRefreshLayout的webView 83 | tvRealTitle = (TextView) view.findViewById(R.id.tv_real_title);//真标题栏 84 | tvFakeTitle = (TextView) view.findViewById(R.id.tv_fake_title);//浮在CoordinatorLayout下面的假标题栏 85 | View llHead = view.findViewById(R.id.ll_head); 86 | 87 | //这个线性布局包裹整个内容,因为在PullToRefreshLayout中,若子view(除了include的头尾布局) 88 | // 的高度小于PullToRefreshLayout的高度时,就会出现不能上拉的情况,所以需要在后面设置最小高度 89 | llItemWhole = (LinearLayout) view.findViewById(R.id.ll_item_whole); 90 | 91 | 92 | switch (currPage) {//根据Page标志设置可上拉/下拉 93 | case FirstPage: 94 | mScrollView.setCanRefresh(false); 95 | llHead.setVisibility(View.VISIBLE); 96 | break; 97 | case CenterPage: 98 | break; 99 | case LastPage: 100 | mScrollView.setCanLoadMore(false); 101 | break; 102 | } 103 | 104 | } 105 | 106 | 107 | private void setListeners() { 108 | pulltoRefresh.setOnRefreshListener(this); 109 | mScrollView.setOnScrollChangeListener(this); 110 | mScrollView.setOnTouchListener(this); 111 | mScrollView.addOnLayoutChangeListener(this); 112 | } 113 | 114 | /** 115 | * 计算真假标题滑动时在窗口位置,当他们相遇时分别做隐藏和显示动作 116 | */ 117 | private void onTitleMeet() { 118 | tvRealTitle.getLocationOnScreen(locationReal); 119 | tvFakeTitle.getLocationOnScreen(locationFake); 120 | if (locationReal[1] <= locationFake[1]) { 121 | if (tvFakeTitle.getVisibility() != View.VISIBLE) 122 | tvFakeTitle.setVisibility(View.VISIBLE); 123 | } else { 124 | if (tvFakeTitle.getVisibility() == View.VISIBLE) 125 | tvFakeTitle.setVisibility(View.INVISIBLE); 126 | } 127 | } 128 | 129 | private void initData() { 130 | Context applicationContext = getContext().getApplicationContext(); 131 | llItemWhole.setMinimumHeight(applicationContext.getResources().getDisplayMetrics().heightPixels); 132 | setRefreshLabel(); 133 | tvFakeTitle.setText(subTitle); 134 | tvRealTitle.setText(subTitle); 135 | webView.setWebViewMargin(0); 136 | webView.loadUrl(pageUrl); 137 | 138 | } 139 | 140 | /** 141 | * 设置上拉下拉提示语 142 | */ 143 | private void setRefreshLabel() { 144 | String loadMoreStr = "加载下一章内容"; 145 | String refreshStr = "加载上一章内容"; 146 | 147 | pulltoRefresh.setLoadMoreWord(loadMoreStr, loadMoreStr, loadMoreStr, loadMoreStr, "加载失败"); 148 | pulltoRefresh.setRefreshWords(refreshStr, refreshStr, refreshStr, refreshStr, "加载失败"); 149 | } 150 | 151 | @Override 152 | public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { 153 | onTitleMeet(); 154 | } 155 | 156 | @Override 157 | public boolean onTouch(View v, MotionEvent event) { 158 | onTitleMeet(); 159 | return false; 160 | } 161 | 162 | 163 | @Override 164 | public void onRefresh(PullToRefreshLayout pullToRefreshLayout) {//下拉PUllToRefreshLayout布局的操作 165 | pulltoRefresh.refreshFinish(PullToRefreshLayout.SUCCEED); 166 | receiverIntent.putExtra("animStyle", AnimStyle.TOP_TO_BOTTOM); 167 | getActivity().sendBroadcast(receiverIntent);//发送广播,通知TabSchoolFragment切换内容 168 | } 169 | 170 | @Override 171 | public void onLoadMore(PullToRefreshLayout pullToRefreshLayout) { 172 | pulltoRefresh.loadmoreFinish(PullToRefreshLayout.SUCCEED); 173 | receiverIntent.putExtra("animStyle", AnimStyle.BOTTOM_TO_TOP); 174 | getActivity().sendBroadcast(receiverIntent); 175 | } 176 | 177 | @Override 178 | public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 179 | onTitleMeet(); 180 | } 181 | 182 | 183 | } 184 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/fragments/TabProjectInfoFragment.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.fragments; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v4.app.Fragment; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import com.tandygong.gzx.nestscroll.R; 11 | 12 | 13 | /** 14 | * Created by gengxin on 2015/12/16. 15 | */ 16 | public class TabProjectInfoFragment extends Fragment { 17 | 18 | @Nullable 19 | @Override 20 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 21 | View view = inflater.inflate(R.layout.fragment_project_info, null); 22 | return view; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/fragments/TabSchoolInfoFragment.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.fragments; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | import android.os.Bundle; 8 | import android.support.annotation.NonNull; 9 | import android.support.annotation.Nullable; 10 | import android.support.v4.app.Fragment; 11 | import android.support.v4.app.FragmentTransaction; 12 | import android.support.v4.widget.DrawerLayout; 13 | import android.util.Log; 14 | import android.view.Gravity; 15 | import android.view.LayoutInflater; 16 | import android.view.View; 17 | import android.view.ViewGroup; 18 | import android.widget.AdapterView; 19 | import android.widget.ListView; 20 | import android.widget.RelativeLayout; 21 | import android.widget.TextView; 22 | 23 | import com.tandygong.gzx.nestscroll.R; 24 | import com.tandygong.gzx.nestscroll.activitys.NestScrollWithScrollViewActivity; 25 | import com.tandygong.gzx.nestscroll.adapters.SubTitleMenuListAdapter; 26 | import com.tandygong.gzx.nestscroll.enums.AnimStyle; 27 | import com.tandygong.gzx.nestscroll.utils.Constants; 28 | 29 | import java.util.ArrayList; 30 | 31 | /** 32 | * Created by gzx on 2016/2/14. 33 | */ 34 | public class TabSchoolInfoFragment extends Fragment implements View.OnClickListener, AdapterView.OnItemClickListener, DrawerLayout.DrawerListener { 35 | 36 | 37 | private ArrayList subTitles; 38 | private ArrayList subUrls; 39 | private int destPageId; 40 | private AnimStyle animStyle; 41 | private int schoolId; 42 | private ListView lvContent; 43 | private DrawerLayout drawerLayout; 44 | private RelativeLayout rlShowSlideMenu; 45 | private TextView tvContentNum; 46 | private BroadcastReceiver receiver; 47 | 48 | @Nullable 49 | @Override 50 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 51 | View view = inflater.inflate(R.layout.fragment_tab_school_info, null); 52 | getExtraData(); 53 | findViewIds(view); 54 | setListeners(); 55 | initData(); 56 | return view; 57 | } 58 | 59 | private void getExtraData() { 60 | Bundle arguments = getArguments(); 61 | if (arguments != null) { 62 | schoolId = arguments.getInt("schoolId"); 63 | } 64 | } 65 | 66 | 67 | private void findViewIds(View view) { 68 | 69 | } 70 | 71 | private void setListeners() { 72 | //注册广播接收者,当收到广播时的操作 73 | receiver = new BroadcastReceiver() { 74 | @Override 75 | public void onReceive(Context context, Intent intent) { 76 | Bundle extras = intent.getExtras(); 77 | if (extras != null) { 78 | animStyle = (AnimStyle) extras.getSerializable("animStyle"); 79 | destPageId += animStyle == AnimStyle.TOP_TO_BOTTOM ? -1 : 1; 80 | } 81 | switchFragment(destPageId, animStyle);//切换内容 82 | } 83 | }; 84 | getContext().registerReceiver(receiver, new IntentFilter(Constants.ACTION_REPLACE_FRAGMENT_SCHOOL_INFO)); 85 | } 86 | 87 | private void initData() { 88 | subTitles = new ArrayList<>(); 89 | subUrls = new ArrayList<>(); 90 | getViewFromActivity(); 91 | setTestData();//设置测试数据 92 | initSlideMenu(); 93 | switchFragment(destPageId, AnimStyle.DEFAULT_NO_ANIM);//首次加载没有切换动画 94 | } 95 | 96 | private void getViewFromActivity() { 97 | NestScrollWithScrollViewActivity activity = (NestScrollWithScrollViewActivity) getActivity(); 98 | lvContent = activity.getLvMenu(); 99 | drawerLayout = activity.getDrawerLayout(); 100 | rlShowSlideMenu = activity.getRlShowSlideMenu(); 101 | tvContentNum = activity.getTvContentNum(); 102 | 103 | drawerLayout.setDrawerListener(this); 104 | rlShowSlideMenu.setOnClickListener(this); 105 | } 106 | 107 | 108 | /** 109 | * 设置DrawerLayout右侧的菜单 110 | */ 111 | private void initSlideMenu() { 112 | lvContent.setAdapter(new SubTitleMenuListAdapter(getActivity(), subTitles)); 113 | lvContent.setOnItemClickListener(this); 114 | } 115 | 116 | 117 | /** 118 | * 根据广播接收的"要求"来定制要显示的fragment 119 | * 120 | * @param destPageId 将要切换内容的页码 121 | * @param animStyle 切换动画效果 122 | */ 123 | private void switchFragment(int destPageId, AnimStyle animStyle) { 124 | if (destPageId >= subUrls.size() || destPageId < 0) { 125 | return; 126 | } 127 | ContainerSchoolInfoFragment fragment = getSchoolInfoContainerFragment(destPageId); 128 | FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction(); 129 | 130 | switch (animStyle) { 131 | case TOP_TO_BOTTOM: 132 | ft.setCustomAnimations(R.anim.slide_in_from_top, R.anim.slide_out_to_bottom); 133 | break; 134 | case BOTTOM_TO_TOP: 135 | ft.setCustomAnimations(R.anim.slide_in_from_bottom, R.anim.slide_out_to_top); 136 | break; 137 | case DEFAULT_NO_ANIM: 138 | break; 139 | } 140 | try { 141 | ft.replace(R.id.fl_container, fragment); 142 | ft.commitAllowingStateLoss(); 143 | tvContentNum.setText(destPageId + 1 + "/" + subUrls.size()); 144 | } catch (Exception e) { 145 | Log.e("exception", e.getMessage());//捕捉页面还没加载就退出activity引起的异常 146 | } 147 | } 148 | 149 | @NonNull 150 | private ContainerSchoolInfoFragment getSchoolInfoContainerFragment(int destPageId) { 151 | ContainerSchoolInfoFragment fragment = new ContainerSchoolInfoFragment(); 152 | Bundle bundle = new Bundle(); 153 | bundle.putString("PageUrl", subUrls.get(destPageId)); 154 | bundle.putInt("TotalCount", subUrls.size()); 155 | bundle.putString("subTitle", subTitles.get(destPageId)); 156 | bundle.putInt("PageId", destPageId); 157 | fragment.setArguments(bundle); 158 | return fragment; 159 | } 160 | 161 | @Override 162 | public void onDestroy() { 163 | getContext().unregisterReceiver(receiver); 164 | super.onDestroy(); 165 | } 166 | 167 | @Override 168 | public void onClick(View v) { 169 | switch (v.getId()) { 170 | case R.id.rl_show_slide_menu: 171 | drawerLayout.openDrawer(Gravity.RIGHT); 172 | break; 173 | } 174 | } 175 | 176 | @Override 177 | public void onItemClick(AdapterView parent, View view, int position, long id) { 178 | SubTitleMenuListAdapter adapter = (SubTitleMenuListAdapter) lvContent.getAdapter(); 179 | if (position != adapter.getSelectPos()) { 180 | adapter.setSelectPos(position); 181 | drawerLayout.closeDrawer(Gravity.RIGHT); 182 | destPageId = position; 183 | switchFragment(destPageId, AnimStyle.DEFAULT_NO_ANIM); 184 | } 185 | } 186 | 187 | @Override 188 | public void onDrawerSlide(View drawerView, float slideOffset) { 189 | rlShowSlideMenu.setAlpha(1f - slideOffset); 190 | } 191 | 192 | @Override 193 | public void onDrawerOpened(View drawerView) { 194 | SubTitleMenuListAdapter adapter = (SubTitleMenuListAdapter) lvContent.getAdapter(); 195 | if (adapter == null) { 196 | return; 197 | } 198 | adapter.setSelectPos(destPageId); 199 | lvContent.smoothScrollToPosition(destPageId); 200 | 201 | } 202 | 203 | @Override 204 | public void onDrawerClosed(View drawerView) { 205 | } 206 | 207 | @Override 208 | public void onDrawerStateChanged(int newState) { 209 | 210 | } 211 | 212 | 213 | private void setTestData() { 214 | subTitles.add("01.概况"); 215 | subTitles.add("02.成都之必体验"); 216 | subTitles.add("04.景点"); 217 | subTitles.add("05.住宿"); 218 | subTitles.add("06.餐饮"); 219 | subTitles.add("07.购物"); 220 | subTitles.add("08.娱乐"); 221 | 222 | subUrls.add("http://m.mafengwo.cn/gl/catalog/index?id=29&catalog_id=100"); 223 | subUrls.add("http://m.mafengwo.cn/gl/catalog/index?id=29&catalog_id=101"); 224 | subUrls.add("http://m.mafengwo.cn/gl/catalog/index?id=29&catalog_id=102"); 225 | subUrls.add("http://m.mafengwo.cn/gl/catalog/index?id=29&catalog_id=1868"); 226 | subUrls.add("http://m.mafengwo.cn/gl/catalog/index?id=29&catalog_id=103"); 227 | subUrls.add("http://m.mafengwo.cn/gl/catalog/index?id=29&catalog_id=104"); 228 | subUrls.add("http://m.mafengwo.cn/gl/catalog/index?id=29&catalog_id=105"); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/utils/ActivityUtils.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import com.tandygong.gzx.nestscroll.activitys.NestScrollWithScrollViewActivity; 8 | 9 | /** 10 | * Created by usb on 2016/4/13. 11 | */ 12 | public class ActivityUtils { 13 | public static void toSchoolDetailActivityForResult(Context context, boolean isApply, String chineseName, String englishName, String imgUrl, int schoolId, int programId, boolean checked) { 14 | Intent intent = new Intent(context, NestScrollWithScrollViewActivity.class); 15 | intent.putExtra("isApply", isApply); 16 | intent.putExtra("chineseName", chineseName); 17 | intent.putExtra("englishName", englishName); 18 | intent.putExtra("imgUrl", imgUrl); 19 | intent.putExtra("schoolId", schoolId); 20 | intent.putExtra("programId", programId); 21 | intent.putExtra("checked", checked); 22 | context.startActivity(intent); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/utils/Constants.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.utils; 2 | 3 | /** 4 | * Created by usb on 2016/4/13. 5 | */ 6 | public class Constants { 7 | public static final String ACTION_REPLACE_FRAGMENT_SCHOOL_INFO = "action_replace_fragment_school_info"; 8 | //WebViewCacheDir 9 | public static final String APP_WEB_CACAHE_DIRNAME = "/webcache"; 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/views/AppWebView.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.views; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.webkit.WebSettings; 6 | import android.webkit.WebView; 7 | 8 | import com.tandygong.gzx.nestscroll.utils.Constants; 9 | 10 | 11 | /** 12 | * Created by gzx on 2016/3/11 at 15:42. 13 | */ 14 | public class AppWebView extends WebView { 15 | 16 | private String cacheDirPath; 17 | 18 | public AppWebView(Context context) { 19 | super(context); 20 | cacheDirPath = context.getFilesDir().getAbsolutePath() + Constants.APP_WEB_CACAHE_DIRNAME; 21 | initSetting(); 22 | } 23 | 24 | public AppWebView(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | cacheDirPath = context.getFilesDir().getAbsolutePath() + Constants.APP_WEB_CACAHE_DIRNAME; 27 | initSetting(); 28 | } 29 | 30 | public AppWebView(Context context, AttributeSet attrs, int defStyleAttr) { 31 | super(context, attrs, defStyleAttr); 32 | cacheDirPath = context.getFilesDir().getAbsolutePath() + Constants.APP_WEB_CACAHE_DIRNAME; 33 | initSetting(); 34 | } 35 | 36 | public void initSetting() { 37 | getSettings().setJavaScriptEnabled(true); 38 | getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH); 39 | getSettings().setCacheMode(WebSettings.LOAD_DEFAULT); // 设置 40 | // 缓存模式 41 | // 开启 DOM storage API 功能 42 | getSettings().setDomStorageEnabled(true); 43 | // 开启 database storage API 功能 44 | getSettings().setDatabaseEnabled(true); 45 | // 设置数据库缓存路径 46 | getSettings().setDatabasePath(cacheDirPath); 47 | // 设置 Application Caches 缓存目录 48 | getSettings().setAppCachePath(cacheDirPath); 49 | // 开启 Application Caches 功能 50 | getSettings().setAppCacheEnabled(true); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/views/AppWebViewClient.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.views; 2 | 3 | import android.graphics.Bitmap; 4 | import android.webkit.WebView; 5 | import android.webkit.WebViewClient; 6 | 7 | /** 8 | * Created by gzx on 2016/3/11 at 15:36. 9 | */ 10 | public class AppWebViewClient extends WebViewClient { 11 | 12 | @Override 13 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 14 | return super.shouldOverrideUrlLoading(view, url); 15 | } 16 | 17 | @Override 18 | public void onPageStarted(WebView view, String url, Bitmap favicon) { 19 | super.onPageStarted(view, url, favicon); 20 | } 21 | 22 | @Override 23 | public void onPageFinished(WebView view, String url) { 24 | super.onPageFinished(view, url); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/views/CustomDrawerLayout.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.views; 2 | 3 | import android.content.Context; 4 | import android.support.v4.widget.DrawerLayout; 5 | import android.util.AttributeSet; 6 | 7 | /** 8 | * Created by gzx on 2016/3/12 at 17:37. 9 | */ 10 | public class CustomDrawerLayout extends DrawerLayout { 11 | 12 | public CustomDrawerLayout(Context context) { 13 | super(context); 14 | } 15 | 16 | public CustomDrawerLayout(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | public CustomDrawerLayout(Context context, AttributeSet attrs, int defStyle) { 21 | super(context, attrs, defStyle); 22 | } 23 | 24 | 25 | @Override 26 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 27 | widthMeasureSpec = MeasureSpec.makeMeasureSpec( 28 | MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY); 29 | heightMeasureSpec = MeasureSpec.makeMeasureSpec( 30 | MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY); 31 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/views/CustomerNestedScrollView.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.views; 2 | 3 | import android.content.Context; 4 | import android.support.v4.widget.NestedScrollView; 5 | import android.util.AttributeSet; 6 | import android.view.MotionEvent; 7 | 8 | /** 9 | * Created by gzx on 2016/3/24 at 20:20. 10 | */ 11 | public class CustomerNestedScrollView extends NestedScrollView implements Pullable { 12 | 13 | private boolean isScrollToBottom; 14 | private boolean canLoadMore = true; 15 | private boolean canRefresh = true; 16 | 17 | 18 | public CustomerNestedScrollView(Context context) { 19 | super(context); 20 | } 21 | 22 | public CustomerNestedScrollView(Context context, AttributeSet attrs) { 23 | super(context, attrs); 24 | } 25 | 26 | public CustomerNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) { 27 | super(context, attrs, defStyleAttr); 28 | } 29 | 30 | 31 | public boolean isCanLoadMore() { 32 | return canLoadMore; 33 | } 34 | 35 | public void setCanLoadMore(boolean canLoadMore) { 36 | this.canLoadMore = canLoadMore; 37 | } 38 | 39 | public boolean isCanRefresh() { 40 | return canRefresh; 41 | } 42 | 43 | public void setCanRefresh(boolean canRefresh) { 44 | this.canRefresh = canRefresh; 45 | } 46 | 47 | 48 | @Override 49 | public boolean canPullDown() { 50 | return getScrollY() == 0 && canRefresh; 51 | } 52 | 53 | 54 | @Override 55 | protected void onScrollChanged(int l, int t, int oldl, int oldt) { 56 | int scrollY = getScrollY(); 57 | super.onScrollChanged(l, t, oldl, oldt); 58 | } 59 | 60 | 61 | @Override 62 | public boolean canPullUp() { 63 | return isScrollToBottom && canLoadMore; 64 | } 65 | 66 | 67 | @Override 68 | public boolean onTouchEvent(MotionEvent ev) { 69 | switch (ev.getAction()) { 70 | case MotionEvent.ACTION_DOWN: 71 | break; 72 | case MotionEvent.ACTION_MOVE: 73 | int scrollY = getScrollY(); 74 | int height = getHeight(); 75 | int scrollViewMeasuredHeight = getChildAt(0).getMeasuredHeight(); 76 | if (scrollY == 0) { 77 | } 78 | if ((scrollY + height) == scrollViewMeasuredHeight) { 79 | isScrollToBottom = true; 80 | } else { 81 | isScrollToBottom = false; 82 | } 83 | break; 84 | 85 | default: 86 | break; 87 | } 88 | return super.onTouchEvent(ev); 89 | } 90 | } -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/views/ProgressBarWebView.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.views; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.os.Handler; 6 | import android.os.Message; 7 | import android.support.v4.content.ContextCompat; 8 | import android.util.AttributeSet; 9 | import android.webkit.WebView; 10 | import android.widget.AbsoluteLayout; 11 | import android.widget.LinearLayout; 12 | import android.widget.ProgressBar; 13 | 14 | import com.tandygong.gzx.nestscroll.R; 15 | 16 | 17 | public class ProgressBarWebView extends AppWebView { 18 | private int webViewMargin = 114; 19 | private ProgressBar progressbar; 20 | private OnLoadCompleteListener onLoadCompleteListener; 21 | Handler handler = new Handler(new Handler.Callback() { 22 | @Override 23 | public boolean handleMessage(Message msg) { 24 | switch (msg.what) { 25 | case 2: 26 | if (getProgress() == 100) { 27 | progressbar.setVisibility(INVISIBLE); 28 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(-1, -1); 29 | params.topMargin = webViewMargin; 30 | setLayoutParams(params); 31 | if (onLoadCompleteListener != null) { 32 | onLoadCompleteListener.onComplete(); 33 | } 34 | } else { 35 | if (progressbar.getVisibility() == INVISIBLE) 36 | progressbar.setVisibility(VISIBLE); 37 | progressbar.setProgress(getProgress()); 38 | handler.sendEmptyMessageDelayed(2, 10); 39 | } 40 | break; 41 | default: 42 | break; 43 | } 44 | 45 | return false; 46 | } 47 | } 48 | ); 49 | 50 | public ProgressBarWebView(Context context) { 51 | this(context, null, 0); 52 | 53 | } 54 | 55 | public void setWebViewMargin(int margin) { 56 | webViewMargin = margin; 57 | } 58 | 59 | public ProgressBarWebView(Context context, AttributeSet attrs) { 60 | this(context, attrs, 0); 61 | } 62 | 63 | public ProgressBarWebView(Context context, AttributeSet attrs, int defStyle) { 64 | super(context, attrs, defStyle); 65 | progressbar = new ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal); 66 | progressbar.setProgressDrawable(ContextCompat.getDrawable(getContext(), R.drawable.progressbar)); 67 | progressbar.setMax(100); 68 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(AbsoluteLayout.LayoutParams.FILL_PARENT, 5); 69 | layoutParams.bottomMargin = 25; 70 | progressbar.setLayoutParams(layoutParams); 71 | addView(progressbar); 72 | setWebViewClient(new WebClient()); 73 | } 74 | 75 | public OnLoadCompleteListener getOnLoadCompleteListener() { 76 | return onLoadCompleteListener; 77 | } 78 | 79 | public void setOnLoadCompleteListener(OnLoadCompleteListener onLoadCompleteListener) { 80 | this.onLoadCompleteListener = onLoadCompleteListener; 81 | } 82 | 83 | 84 | public class WebClient extends AppWebViewClient { 85 | @Override 86 | public void onPageFinished(WebView view, String url) { 87 | super.onPageFinished(view, url); 88 | } 89 | 90 | @Override 91 | public void onPageStarted(WebView view, String url, Bitmap favicon) { 92 | handler.sendEmptyMessage(2); 93 | super.onPageStarted(view, url, favicon); 94 | } 95 | 96 | } 97 | 98 | 99 | @Override 100 | protected void onScrollChanged(int l, int t, int oldl, int oldt) { 101 | AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams) progressbar.getLayoutParams(); 102 | lp.x = l; 103 | lp.y = t; 104 | progressbar.setLayoutParams(lp); 105 | super.onScrollChanged(l, t, oldl, oldt); 106 | } 107 | 108 | 109 | public interface OnLoadCompleteListener { 110 | void onComplete(); 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/views/PullToRefreshLayout.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.views; 2 | 3 | import android.content.Context; 4 | import android.os.AsyncTask; 5 | import android.os.Handler; 6 | import android.os.Message; 7 | import android.util.AttributeSet; 8 | import android.view.MotionEvent; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.view.animation.AnimationUtils; 12 | import android.view.animation.LinearInterpolator; 13 | import android.view.animation.RotateAnimation; 14 | import android.widget.RelativeLayout; 15 | import android.widget.TextView; 16 | 17 | 18 | import com.tandygong.gzx.nestscroll.R; 19 | 20 | import java.util.Timer; 21 | import java.util.TimerTask; 22 | 23 | public class PullToRefreshLayout extends RelativeLayout { 24 | private String mStrRefreshing = "刷新中"; 25 | private String mStrPullToRefresh = "继续下拉以刷新"; 26 | private String mStrReleaseToRefresh = "释放刷新"; 27 | private String mStrRefreshSuccess = "刷新成功"; 28 | private String mStrRefreshFail="刷新失败"; 29 | 30 | private String mStrLoading = "加载中"; 31 | private String mStrPullToLoadMore = "继续上拉加载更多"; 32 | private String mStrReleaseToLoadMore = "释放加载更多"; 33 | private String mStrLoadSuccess = "加载成功"; 34 | private String mStrLoadFail="加载失败"; 35 | 36 | // 初始状态 37 | public static final int INIT = 0; 38 | // 释放刷新 39 | public static final int RELEASE_TO_REFRESH = 1; 40 | // 正在刷新 41 | public static final int REFRESHING = 2; 42 | // 释放加载 43 | public static final int RELEASE_TO_LOAD = 3; 44 | // 正在加载 45 | public static final int LOADING = 4; 46 | // 操作完毕 47 | public static final int DONE = 5; 48 | // 当前状态 49 | private int state = INIT; 50 | // 刷新回调接口 51 | private OnRefreshListener mListener; 52 | // 刷新成功 53 | public static final int SUCCEED = 0; 54 | // 刷新失败 55 | public static final int FAIL = 1; 56 | // 按下Y坐标,上一个事件点Y坐标 57 | private float downY, lastY; 58 | 59 | // 下拉的距离。注意:pullDownY和pullUpY不可能同时不为0 60 | public float pullDownY = 0; 61 | // 上拉的距离 62 | private float pullUpY = 0; 63 | 64 | // 释放刷新的距离 65 | private float refreshDist = 200; 66 | // 释放加载的距离 67 | private float loadmoreDist = 200; 68 | 69 | private MyTimer timer; 70 | // 回滚速度 71 | public float MOVE_SPEED = 8; 72 | // 第一次执行布局 73 | private boolean isLayout = false; 74 | // 在刷新过程中滑动操作 75 | private boolean isTouch = false; 76 | // 手指滑动距离与下拉头的滑动距离比,中间会随正切函数变化 77 | private float radio = 2; 78 | 79 | // 下拉箭头的转180°动画 80 | private RotateAnimation rotateAnimation; 81 | // 均匀旋转动画 82 | private RotateAnimation refreshingAnimation; 83 | 84 | // 下拉头 85 | private View refreshView; 86 | // 下拉的箭头 87 | private View pullView; 88 | // 正在刷新的图标 89 | private View refreshingView; 90 | // 刷新结果图标 91 | private View refreshStateImageView; 92 | // 刷新结果:成功或失败 93 | private TextView refreshStateTextView; 94 | 95 | // 上拉头 96 | private View loadmoreView; 97 | // 上拉的箭头 98 | private View pullUpView; 99 | // 正在加载的图标 100 | private View loadingView; 101 | // 加载结果图标 102 | private View loadStateImageView; 103 | // 加载结果:成功或失败 104 | private TextView loadStateTextView; 105 | 106 | // 实现了Pullable接口的View 107 | private View pullableView; 108 | // 过滤多点触碰 109 | private int mEvents; 110 | 111 | // 这两个变量用来控制pull的方向,如果不加控制,当情况满足可上拉又可下拉时没法下拉 112 | private boolean canPullDown = true; 113 | private boolean canPullUp = true; 114 | 115 | private Context mContext; 116 | 117 | Handler updateHandler = new Handler() { 118 | 119 | @Override 120 | public void handleMessage(Message msg) { 121 | // 回弹速度随下拉距离moveDeltaY增大而增大 122 | MOVE_SPEED = (float) (8 + 5 * Math.tan(Math.PI / 2 123 | / getMeasuredHeight() * (pullDownY + Math.abs(pullUpY)))); 124 | if (!isTouch) { 125 | // 正在刷新,且没有往上推的话则悬停,显示"正在刷新..." 126 | if (state == REFRESHING && pullDownY <= refreshDist) { 127 | pullDownY = refreshDist; 128 | timer.cancel(); 129 | } else if (state == LOADING && -pullUpY <= loadmoreDist) { 130 | pullUpY = -loadmoreDist; 131 | timer.cancel(); 132 | } 133 | 134 | } 135 | if (pullDownY > 0) 136 | pullDownY -= MOVE_SPEED; 137 | else if (pullUpY < 0) 138 | pullUpY += MOVE_SPEED; 139 | if (pullDownY < 0) { 140 | // 已完成回弹 141 | pullDownY = 0; 142 | pullView.clearAnimation(); 143 | // 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态 144 | if (state != REFRESHING && state != LOADING) 145 | changeState(INIT); 146 | timer.cancel(); 147 | requestLayout(); 148 | } 149 | if (pullUpY > 0) { 150 | // 已完成回弹 151 | pullUpY = 0; 152 | pullUpView.clearAnimation(); 153 | // 隐藏上拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态 154 | if (state != REFRESHING && state != LOADING) 155 | changeState(INIT); 156 | timer.cancel(); 157 | requestLayout(); 158 | } 159 | // Log.d("handle", "handle"); 160 | // 刷新布局,会自动调用onLayout 161 | requestLayout(); 162 | // 没有拖拉或者回弹完成 163 | if (pullDownY + Math.abs(pullUpY) == 0) 164 | timer.cancel(); 165 | } 166 | 167 | }; 168 | 169 | public void setmStrRefreshing(String mStrRefreshing) { 170 | this.mStrRefreshing = mStrRefreshing; 171 | } 172 | 173 | public void setmStrPullToRefresh(String mStrPullToRefresh) { 174 | this.mStrPullToRefresh = mStrPullToRefresh; 175 | } 176 | 177 | public void setmStrReleaseToRefresh(String mStrReleaseToRefresh) { 178 | this.mStrReleaseToRefresh = mStrReleaseToRefresh; 179 | } 180 | 181 | public void setmStrRefreshSuccess(String mStrRefreshSuccess) { 182 | this.mStrRefreshSuccess = mStrRefreshSuccess; 183 | } 184 | 185 | public void setmStrRefreshFail(String mStrRefreshFail) { 186 | this.mStrRefreshFail = mStrRefreshFail; 187 | } 188 | 189 | public void setmStrLoading(String mStrLoading) { 190 | this.mStrLoading = mStrLoading; 191 | } 192 | 193 | public void setmStrPullToLoadMore(String mStrPullToLoadMore) { 194 | this.mStrPullToLoadMore = mStrPullToLoadMore; 195 | } 196 | 197 | public void setmStrReleaseToLoadMore(String mStrReleaseToLoadMore) { 198 | this.mStrReleaseToLoadMore = mStrReleaseToLoadMore; 199 | } 200 | 201 | public void setmStrLoadSuccess(String mStrLoadSuccess) { 202 | this.mStrLoadSuccess = mStrLoadSuccess; 203 | } 204 | 205 | public void setmStrLoadFail(String mStrLoadFail) { 206 | this.mStrLoadFail = mStrLoadFail; 207 | } 208 | public void setRefreshWords(String refreshingStr, String pulltoRefreshStr, String releasetoRefreshStr, String refreshSuccessStr,String refreshFailStr) { 209 | this.mStrRefreshing = refreshingStr; 210 | this.mStrPullToRefresh = pulltoRefreshStr; 211 | this.mStrReleaseToRefresh = releasetoRefreshStr; 212 | this.mStrRefreshSuccess = refreshSuccessStr; 213 | this.mStrRefreshFail=refreshFailStr; 214 | } 215 | 216 | public void setLoadMoreWord(String loadingStr, String pulltoLoadmoreStr, String releaseToLoadmoreStr, String loadSuccessStr,String loadFailStr) { 217 | this.mStrLoading = loadingStr; 218 | this.mStrPullToLoadMore = pulltoLoadmoreStr; 219 | this.mStrReleaseToLoadMore = releaseToLoadmoreStr; 220 | this.mStrLoadSuccess = loadSuccessStr; 221 | this.mStrLoadFail=loadFailStr; 222 | } 223 | 224 | public void setOnRefreshListener(OnRefreshListener listener) { 225 | mListener = listener; 226 | } 227 | 228 | public PullToRefreshLayout(Context context) { 229 | super(context); 230 | initView(context); 231 | } 232 | 233 | public PullToRefreshLayout(Context context, AttributeSet attrs) { 234 | super(context, attrs); 235 | initView(context); 236 | } 237 | 238 | public PullToRefreshLayout(Context context, AttributeSet attrs, int defStyle) { 239 | super(context, attrs, defStyle); 240 | initView(context); 241 | } 242 | 243 | private void initView(Context context) { 244 | mContext = context; 245 | timer = new MyTimer(updateHandler); 246 | rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation( 247 | context, R.anim.reverse_anim); 248 | refreshingAnimation = (RotateAnimation) AnimationUtils.loadAnimation( 249 | context, R.anim.rotating); 250 | // 添加匀速转动动画 251 | LinearInterpolator lir = new LinearInterpolator(); 252 | rotateAnimation.setInterpolator(lir); 253 | refreshingAnimation.setInterpolator(lir); 254 | } 255 | 256 | private void hide() { 257 | timer.schedule(5); 258 | } 259 | 260 | 261 | public void refreshFinish(int refreshResult) { 262 | refreshingView.clearAnimation(); 263 | refreshingView.setVisibility(View.GONE); 264 | switch (refreshResult) { 265 | case SUCCEED: 266 | // 刷新成功 267 | refreshStateImageView.setVisibility(View.VISIBLE); 268 | refreshStateTextView.setText(mStrRefreshSuccess); 269 | refreshStateImageView 270 | .setBackgroundResource(R.mipmap.refresh_succeed); 271 | break; 272 | case FAIL: 273 | default: 274 | // 刷新失败 275 | refreshStateImageView.setVisibility(View.VISIBLE); 276 | refreshStateTextView.setText(mStrRefreshFail); 277 | refreshStateImageView 278 | .setBackgroundResource(R.mipmap.refresh_failed); 279 | break; 280 | } 281 | if (pullDownY > 0) { 282 | // 刷新结果停留1秒 283 | new Handler() { 284 | @Override 285 | public void handleMessage(Message msg) { 286 | changeState(DONE); 287 | hide(); 288 | } 289 | }.sendEmptyMessageDelayed(0, 1000); 290 | } else { 291 | changeState(DONE); 292 | hide(); 293 | } 294 | } 295 | 296 | 297 | public void loadmoreFinish(int refreshResult) { 298 | loadingView.clearAnimation(); 299 | loadingView.setVisibility(View.GONE); 300 | switch (refreshResult) { 301 | case SUCCEED: 302 | // 加载成功 303 | loadStateImageView.setVisibility(View.VISIBLE); 304 | loadStateTextView.setText(mStrLoadSuccess); 305 | loadStateImageView.setBackgroundResource(R.mipmap.load_succeed); 306 | break; 307 | case FAIL: 308 | default: 309 | // 加载失败 310 | loadStateImageView.setVisibility(View.VISIBLE); 311 | loadStateTextView.setText(mStrLoadFail); 312 | loadStateImageView.setBackgroundResource(R.mipmap.load_failed); 313 | break; 314 | } 315 | if (pullUpY < 0) { 316 | // 刷新结果停留1秒 317 | new Handler() { 318 | @Override 319 | public void handleMessage(Message msg) { 320 | changeState(DONE); 321 | hide(); 322 | } 323 | }.sendEmptyMessageDelayed(0, 1000); 324 | } else { 325 | changeState(DONE); 326 | hide(); 327 | } 328 | } 329 | 330 | private void changeState(int to) { 331 | state = to; 332 | switch (state) { 333 | case INIT: 334 | // 下拉布局初始状态 335 | refreshStateImageView.setVisibility(View.GONE); 336 | refreshStateTextView.setText(mStrPullToRefresh); 337 | pullView.clearAnimation(); 338 | pullView.setVisibility(View.VISIBLE); 339 | // 上拉布局初始状态 340 | loadStateImageView.setVisibility(View.GONE); 341 | loadStateTextView.setText(mStrPullToLoadMore); 342 | pullUpView.clearAnimation(); 343 | pullUpView.setVisibility(View.VISIBLE); 344 | break; 345 | case RELEASE_TO_REFRESH: 346 | // 释放刷新状态 347 | refreshStateTextView.setText(mStrReleaseToRefresh); 348 | pullView.startAnimation(rotateAnimation); 349 | break; 350 | case REFRESHING: 351 | // 正在刷新状态 352 | pullView.clearAnimation(); 353 | refreshingView.setVisibility(View.VISIBLE); 354 | pullView.setVisibility(View.INVISIBLE); 355 | refreshingView.startAnimation(refreshingAnimation); 356 | refreshStateTextView.setText(mStrRefreshing); 357 | break; 358 | case RELEASE_TO_LOAD: 359 | // 释放加载状态 360 | loadStateTextView.setText(mStrReleaseToLoadMore); 361 | pullUpView.startAnimation(rotateAnimation); 362 | break; 363 | case LOADING: 364 | // 正在加载状态 365 | pullUpView.clearAnimation(); 366 | loadingView.setVisibility(View.VISIBLE); 367 | pullUpView.setVisibility(View.INVISIBLE); 368 | loadingView.startAnimation(refreshingAnimation); 369 | loadStateTextView.setText(mStrLoading); 370 | break; 371 | case DONE: 372 | // 刷新或加载完毕,啥都不做 373 | break; 374 | } 375 | } 376 | 377 | private void releasePull() { 378 | canPullDown = true; 379 | canPullUp = true; 380 | } 381 | 382 | 383 | @Override 384 | public boolean dispatchTouchEvent(MotionEvent ev) { 385 | switch (ev.getActionMasked()) { 386 | case MotionEvent.ACTION_DOWN: 387 | downY = ev.getY(); 388 | lastY = downY; 389 | timer.cancel(); 390 | mEvents = 0; 391 | releasePull(); 392 | break; 393 | case MotionEvent.ACTION_POINTER_DOWN: 394 | case MotionEvent.ACTION_POINTER_UP: 395 | // 过滤多点触碰 396 | mEvents = -1; 397 | break; 398 | case MotionEvent.ACTION_MOVE: 399 | if (mEvents == 0) { 400 | if (pullDownY > 0 401 | || (((Pullable) pullableView).canPullDown() 402 | && canPullDown && state != LOADING)) { 403 | // 可以下拉,正在加载时不能下拉 404 | // 对实际滑动距离做缩小,造成用力拉的感觉 405 | pullDownY = pullDownY + (ev.getY() - lastY) / radio; 406 | if (pullDownY < 0) { 407 | pullDownY = 0; 408 | canPullDown = false; 409 | canPullUp = true; 410 | } 411 | if (pullDownY > getMeasuredHeight()) 412 | pullDownY = getMeasuredHeight(); 413 | if (state == REFRESHING) { 414 | // 正在刷新的时候触摸移动 415 | isTouch = true; 416 | } 417 | } else if (pullUpY < 0 418 | || (((Pullable) pullableView).canPullUp() && canPullUp && state != REFRESHING)) { 419 | // 可以上拉,正在刷新时不能上拉 420 | pullUpY = pullUpY + (ev.getY() - lastY) / radio; 421 | if (pullUpY > 0) { 422 | pullUpY = 0; 423 | canPullDown = true; 424 | canPullUp = false; 425 | } 426 | if (pullUpY < -getMeasuredHeight()) 427 | pullUpY = -getMeasuredHeight(); 428 | if (state == LOADING) { 429 | // 正在加载的时候触摸移动 430 | isTouch = true; 431 | } 432 | } else 433 | releasePull(); 434 | } else 435 | mEvents = 0; 436 | lastY = ev.getY(); 437 | // 根据下拉距离改变比例 438 | radio = (float) (2 + 2 * Math.tan(Math.PI / 2 / getMeasuredHeight() 439 | * (pullDownY + Math.abs(pullUpY)))); 440 | if (pullDownY > 0 || pullUpY < 0) 441 | requestLayout(); 442 | if (pullDownY > 0) { 443 | if (pullDownY <= refreshDist 444 | && (state == RELEASE_TO_REFRESH || state == DONE)) { 445 | // 如果下拉距离没达到刷新的距离且当前状态是释放刷新,改变状态为下拉刷新 446 | changeState(INIT); 447 | } 448 | if (pullDownY >= refreshDist && state == INIT) { 449 | // 如果下拉距离达到刷新的距离且当前状态是初始状态刷新,改变状态为释放刷新 450 | changeState(RELEASE_TO_REFRESH); 451 | } 452 | } else if (pullUpY < 0) { 453 | // 下面是判断上拉加载的,同上,注意pullUpY是负值 454 | if (-pullUpY <= loadmoreDist 455 | && (state == RELEASE_TO_LOAD || state == DONE)) { 456 | changeState(INIT); 457 | } 458 | // 上拉操作 459 | if (-pullUpY >= loadmoreDist && state == INIT) { 460 | changeState(RELEASE_TO_LOAD); 461 | } 462 | 463 | } 464 | // 因为刷新和加载操作不能同时进行,所以pullDownY和pullUpY不会同时不为0,因此这里用(pullDownY + 465 | // Math.abs(pullUpY))就可以不对当前状态作区分了 466 | if ((pullDownY + Math.abs(pullUpY)) > 8) { 467 | // 防止下拉过程中误触发长按事件和点击事件 468 | ev.setAction(MotionEvent.ACTION_CANCEL); 469 | } 470 | break; 471 | case MotionEvent.ACTION_UP: 472 | if (pullDownY > refreshDist || -pullUpY > loadmoreDist) 473 | // 正在刷新时往下拉(正在加载时往上拉),释放后下拉头(上拉头)不隐藏 474 | { 475 | isTouch = false; 476 | } 477 | if (state == RELEASE_TO_REFRESH) { 478 | changeState(REFRESHING); 479 | // 刷新操作 480 | if (mListener != null) 481 | mListener.onRefresh(this); 482 | } else if (state == RELEASE_TO_LOAD) { 483 | changeState(LOADING); 484 | // 加载操作 485 | if (mListener != null) 486 | mListener.onLoadMore(this); 487 | } 488 | hide(); 489 | default: 490 | break; 491 | } 492 | // 事件分发交给父类 493 | super.dispatchTouchEvent(ev); 494 | return true; 495 | } 496 | 497 | private class AutoRefreshAndLoadTask extends 498 | AsyncTask { 499 | 500 | @Override 501 | protected String doInBackground(Integer... params) { 502 | while (pullDownY < 4 / 3 * refreshDist) { 503 | pullDownY += MOVE_SPEED; 504 | publishProgress(pullDownY); 505 | try { 506 | Thread.sleep(params[0]); 507 | } catch (InterruptedException e) { 508 | e.printStackTrace(); 509 | } 510 | } 511 | return null; 512 | } 513 | 514 | @Override 515 | protected void onPostExecute(String result) { 516 | changeState(REFRESHING); 517 | if (mListener != null) 518 | mListener.onRefresh(PullToRefreshLayout.this); 519 | hide(); 520 | } 521 | 522 | @Override 523 | protected void onProgressUpdate(Float... values) { 524 | if (pullDownY > refreshDist) 525 | changeState(RELEASE_TO_REFRESH); 526 | requestLayout(); 527 | } 528 | 529 | } 530 | 531 | public void autoRefresh() { 532 | AutoRefreshAndLoadTask task = new AutoRefreshAndLoadTask(); 533 | task.execute(20); 534 | } 535 | 536 | public void autoLoad() { 537 | pullUpY = -loadmoreDist; 538 | requestLayout(); 539 | changeState(LOADING); 540 | // 加载操作 541 | if (mListener != null) 542 | mListener.onLoadMore(this); 543 | } 544 | 545 | private void initView() { 546 | // 初始化下拉布局 547 | pullView = refreshView.findViewById(R.id.pull_icon); 548 | refreshStateTextView = (TextView) refreshView 549 | .findViewById(R.id.state_tv); 550 | refreshingView = refreshView.findViewById(R.id.refreshing_icon); 551 | refreshStateImageView = refreshView.findViewById(R.id.state_iv); 552 | // 初始化上拉布局 553 | pullUpView = loadmoreView.findViewById(R.id.pullup_icon); 554 | loadStateTextView = (TextView) loadmoreView 555 | .findViewById(R.id.loadstate_tv); 556 | loadingView = loadmoreView.findViewById(R.id.loading_icon); 557 | loadStateImageView = loadmoreView.findViewById(R.id.loadstate_iv); 558 | } 559 | 560 | @Override 561 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 562 | if (!isLayout) { 563 | // 这里是第一次进来的时候做一些初始化 564 | refreshView = getChildAt(0); 565 | pullableView = getChildAt(1); 566 | loadmoreView = getChildAt(2); 567 | isLayout = true; 568 | initView(); 569 | refreshDist = ((ViewGroup) refreshView).getChildAt(0) 570 | .getMeasuredHeight(); 571 | loadmoreDist = ((ViewGroup) loadmoreView).getChildAt(0) 572 | .getMeasuredHeight(); 573 | } 574 | // 改变子控件的布局,这里直接用(pullDownY + pullUpY)作为偏移量,这样就可以不对当前状态作区分 575 | refreshView.layout(0, 576 | (int) (pullDownY + pullUpY) - refreshView.getMeasuredHeight(), 577 | refreshView.getMeasuredWidth(), (int) (pullDownY + pullUpY)); 578 | pullableView.layout(0, (int) (pullDownY + pullUpY), 579 | pullableView.getMeasuredWidth(), (int) (pullDownY + pullUpY) 580 | + pullableView.getMeasuredHeight()); 581 | loadmoreView.layout(0, 582 | (int) (pullDownY + pullUpY) + pullableView.getMeasuredHeight(), 583 | loadmoreView.getMeasuredWidth(), 584 | (int) (pullDownY + pullUpY) + pullableView.getMeasuredHeight() 585 | + loadmoreView.getMeasuredHeight()); 586 | } 587 | 588 | class MyTimer { 589 | private Handler handler; 590 | private Timer timer; 591 | private MyTask mTask; 592 | 593 | public MyTimer(Handler handler) { 594 | this.handler = handler; 595 | timer = new Timer(); 596 | } 597 | 598 | public void schedule(long period) { 599 | if (mTask != null) { 600 | mTask.cancel(); 601 | mTask = null; 602 | } 603 | mTask = new MyTask(handler); 604 | timer.schedule(mTask, 0, period); 605 | } 606 | 607 | public void cancel() { 608 | if (mTask != null) { 609 | mTask.cancel(); 610 | mTask = null; 611 | } 612 | } 613 | 614 | class MyTask extends TimerTask { 615 | private Handler handler; 616 | 617 | public MyTask(Handler handler) { 618 | this.handler = handler; 619 | } 620 | 621 | @Override 622 | public void run() { 623 | handler.obtainMessage().sendToTarget(); 624 | } 625 | 626 | } 627 | } 628 | 629 | public interface OnRefreshListener { 630 | 631 | void onRefresh(PullToRefreshLayout pullToRefreshLayout); 632 | 633 | void onLoadMore(PullToRefreshLayout pullToRefreshLayout); 634 | } 635 | 636 | } 637 | -------------------------------------------------------------------------------- /app/src/main/java/com/tandygong/gzx/nestscroll/views/Pullable.java: -------------------------------------------------------------------------------- 1 | package com.tandygong.gzx.nestscroll.views; 2 | 3 | public interface Pullable 4 | { 5 | boolean canPullDown(); 6 | boolean canPullUp(); 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/reverse_anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/rotating.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_from_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_from_top.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_to_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_to_top.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_black_radius_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/progressbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 |